"""
Test CIM objects (e.g. `CIMInstance`).

Note that class `NocaseDict` is tested in test_nocasedict.py.
"""

import sys
import re
from datetime import timedelta, datetime
from unittest.mock import patch
from collections import OrderedDict

import pytest
from packaging.version import parse as parse_version

from ..utils.validate import validate_cim_xml_obj
from ..utils.pytest_extensions import simplified_test_function, log_entry_exit

# pylint: disable=wrong-import-position, wrong-import-order, invalid-name
from ...utils import import_installed
pywbem = import_installed('pywbem')
from pywbem import CIMInstance, CIMInstanceName, CIMClass, CIMClassName, \
    CIMProperty, CIMMethod, CIMParameter, CIMQualifier, \
    CIMQualifierDeclaration, Uint8, Uint16, Uint32, Uint64, Sint8, Sint16, \
    Sint32, Sint64, Real32, Real64, Char16, CIMDateTime, \
    MinutesFromUTC, __version__, MissingKeybindingsWarning  # noqa: E402
from pywbem._nocasedict import NocaseDict  # noqa: E402
from pywbem._cim_obj import mofstr  # noqa: E402
from pywbem._utils import _format  # noqa: E402
from pywbem import cimvalue  # noqa: E402
# pylint: enable=wrong-import-position, wrong-import-order, invalid-name

# Literal form {"blah: 0} faster than dict(blah=0) but same functionality
# pylint: disable=use-dict-literal

# Allows use of lots of single character variable names.
# pylint: disable=invalid-name,missing-docstring,too-many-statements
# pylint: disable=too-many-lines,no-self-use


# A note on using pytest.warns:
#
# It turns out that catching Python warnings within test cases is a tricky
# thing.
#
# The main mechanism to understand is that Python warnings that are issued
# will be registered in a "warning registry" under certain circumstances. The
# "warning registry" is a variable named `__warningregistry__` in the global
# namespace of the module that issued the warning. A tuple of warning message,
# warning type, and line number where the warning is issued, is used as a key
# into that dictionary. If a warning is issued using `warnings.warn()`, and
# the "warning registry" already has the key for that warning (i.e. it has
# been previously been issued and registered), the warning is silently ignored,
# and thus will not be caught by the Python `warnings.catch_warnings` or
# `pytest.warns` context managers. This is of course undesired in test cases
# that use these context managers to verify that the warning was issued.
#
# The circumstances under which a warning is registered in the "warning
# registry" depend on the action that is set for that warning.
# For example, action "default" will cause the warning to be registered, and
# action "always" will not cause it to be registered.
# The `pytest.warns` context manager adds a filter with action "always" for all
# warnings upon entry, and removes that filter again upon exit.
#
# One solution is to catch all warnings that are ever issued, across all
# test cases that are executed in a single Python process invocation, with a
# `pytest.warns` context manager.
#
# Another solution is to set the action for all warnings to "always",
# e.g. by following the recommendation in
# https://docs.pytest.org/en/3.0.0/recwarn.html#ensuring-a-function-triggers-a-deprecation-warning
# namely to perform:
#    warnings.simplefilter('always')
# in each test case. The downside of this approach is that pytest displays a
# warning summary at the end for warnings that are issued that way.

# UserWarning issued by NocaseDict when order of input items is not preserved
NOCASEDICT_ORDER_WARNING = UserWarning if sys.version_info[0:2] < (3, 7) \
    else None

unimplemented = pytest.mark.skipif(True, reason="test not implemented")

# Tuple with pywbem version info (M, N, P), without any dev version.
# Can be used in testcase conditions for version specific tests.
# Note for dev versions (e.g. '0.15.0.dev12'):
# - Before 0.15.0, dev versions of an upcoming version always showed
#   the next patch version, which was not always the intended next version.
# - Starting with 0.15.0, dev versions of an upcoming version always show the
#   intended next version.
version_info = parse_version(__version__).release

# Values for expected 'type' property; since 0.12 they are converted to unicode
exp_type_char16 = 'char16'
exp_type_string = 'string'
exp_type_boolean = 'boolean'
exp_type_uint8 = 'uint8'
exp_type_uint16 = 'uint16'
exp_type_uint32 = 'uint32'
exp_type_uint64 = 'uint64'
exp_type_sint8 = 'sint8'
exp_type_sint16 = 'sint16'
exp_type_sint32 = 'sint32'
exp_type_sint64 = 'sint64'
exp_type_real32 = 'real32'
exp_type_real64 = 'real64'
exp_type_datetime = 'datetime'
exp_type_reference = 'reference'

# Values for expected 'embedded_object' property; since 0.12 they are converted
# to unicode
exp_eo_object = 'object'
exp_eo_instance = 'instance'

# Ranges of the CIM integer datatypes

MAX_UINT8 = 2**8 - 1
MAX_UINT16 = 2**16 - 1
MAX_UINT32 = 2**32 - 1
MAX_UINT64 = 2**64 - 1

MIN_SINT8 = -2**7
MIN_SINT16 = -2**15
MIN_SINT32 = -2**31
MIN_SINT64 = -2**63

MAX_SINT8 = 2**7 - 1
MAX_SINT16 = 2**15 - 1
MAX_SINT32 = 2**31 - 1
MAX_SINT64 = 2**63 - 1

# Common constant objects for use in the test cases

DATETIME1_DT = datetime(year=2014, month=9, day=24, hour=19, minute=30,
                        second=40, microsecond=654321,
                        tzinfo=MinutesFromUTC(120))
DATETIME1_OBJ = CIMDateTime(DATETIME1_DT)
DATETIME1_STR = '20140924193040.654321+120'

DATETIME2_DT = datetime(year=2020, month=1, day=28, hour=14, minute=46,
                        second=40, microsecond=654321,
                        tzinfo=MinutesFromUTC(120))
DATETIME2_OBJ = CIMDateTime(DATETIME2_DT)
DATETIME2_STR = '20200128144640.654321+120'

TIMEDELTA1_TD = timedelta(183, (13 * 60 + 25) * 60 + 42, 234567)
TIMEDELTA1_OBJ = CIMDateTime(TIMEDELTA1_TD)
TIMEDELTA1_STR = '00000183132542.234567:000'

UNNAMED_KEY_NCD = NocaseDict()
UNNAMED_KEY_NCD.allow_unnamed_keys = True
UNNAMED_KEY_NCD[None] = 'abc'

# Simplest possible CIM objects
CIMQUALIFIER_Q1_OBJ = CIMQualifier('Q1', value='abc')
CIMINSTANCE_C1_OBJ = CIMInstance('C1')
CIMCLASS_C1_OBJ = CIMClass('C1')
CIMINSTANCENAME_C1_OBJ = CIMInstanceName('C1')
CIMCLASSNAME_C1_OBJ = CIMClassName('C1')
CIMPROPERTY_P1_OBJ = CIMProperty('P1', value='abc')
CIMMETHOD_M1_OBJ = CIMMethod('M1', return_type='string')
CIMPARAMETER_P1_OBJ = CIMParameter('P1', type='uint32')

REF1_STR = 'http://host/ns:CIM_Ref1.k1="abc"'
REF1_OBJ = CIMInstanceName(classname='CIM_Ref1', keybindings=dict(k1="abc"),
                           namespace='ns', host='host')
REF2_OBJ = CIMClassName('CIM_Ref2')


def swapcase2(text):
    """
    Returns the input text, where every other character starting from the first
    character has been changed to swap its lexical case. For strings that
    contain at least one letter, the returned string is guaranteed to be
    different from the input string.
    """
    text_cs = ''
    i = 0
    for c in text:
        if i % 2 == 0:
            c = c.swapcase()
        text_cs += c
        i += 1
    return text_cs


TESTCASES_DICT = [

    # Testcases for dictionary tests

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: Object that has dictionary behavior, e.g. IMInstanceName.
    #   * exp_dict: Expected dictionary items, for validation.
    # * 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

    (
        "CIMInstanceName with two keybindings",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                keybindings=dict(
                    Chicken='Ham',
                    Beans=Uint8(42),
                ),
            ),
            exp_dict=dict(
                Chicken='Ham',
                Beans=Uint8(42),
            ),
        ),
        None, NOCASEDICT_ORDER_WARNING, True
    ),
    (
        "CIMInstance with two properties",
        dict(
            obj=CIMInstance(
                'CIM_Foo',
                properties=dict(
                    Chicken='Ham',
                    Beans=Uint32(42),
                ),
            ),
            exp_dict=dict(
                Chicken='Ham',
                Beans=Uint32(42),
            ),
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICT)
@simplified_test_function
@log_entry_exit
def test_dict(testcase, obj, exp_dict):
    # pylint: disable=unused-argument
    """
    Test function for dictionary tests.

    Treat obj as a dict and run dictionary tests, and compare the
    dict content with exp_dict.

    Raises a test failure if the test fails.

    Parameters:

      * obj (dict): The dictionary-like CIM object to be tested
        (e.g. `CIMInstance`).

      * exp_dict (dict): The expected content of the dictionary.
    """

    # Test __getitem__()

    for key in exp_dict:
        assert obj[key] == exp_dict[key]
        assert obj[swapcase2(key)] == exp_dict[key]

    with pytest.raises(KeyError):
        _ = obj['undefined_Cheepy']  # undefined key

    # Test __setitem__()

    new_key = 'tmp'
    new_value = 'tmp_value'

    obj[new_key] = new_value

    assert obj[new_key] == new_value
    assert obj[swapcase2(new_key)] == new_value

    # Test has_key()

    assert obj.has_key(new_key)  # noqa: W601
    assert obj.has_key(swapcase2(new_key))  # noqa: W601

    # Test __delitem__()

    del obj[swapcase2(new_key)]

    assert not obj.has_key(new_key)  # noqa: W601
    assert not obj.has_key(swapcase2(new_key))  # noqa: W601

    # Test __len__()

    assert len(obj) == len(exp_dict)

    # Test keys() iteration

    keys = list(obj.keys())
    assert len(keys) == 2
    for key in exp_dict:
        assert key in keys

    # Test keys() containment

    for key in exp_dict:
        assert key in obj.keys()

    # Test values() iteration

    values = list(obj.values())
    assert len(values) == 2
    for key in exp_dict:
        assert exp_dict[key] in values

    # Test values() containment

    for key in exp_dict:
        assert exp_dict[key] in obj.values()

    # Test items() iteration

    items = list(obj.items())
    assert len(items) == 2
    for key in exp_dict:
        assert (key, exp_dict[key]) in items

    # Test items() containment

    for key in exp_dict:
        assert (key, exp_dict[key]) in obj.items()

    # Test iterkeys() iteration

    with pytest.warns(DeprecationWarning):
        iterkeys = list(obj.iterkeys())
        assert len(iterkeys) == 2
        for key in exp_dict:
            assert key in iterkeys

    # Test iterkeys() containment

    with pytest.warns(DeprecationWarning):
        for key in exp_dict:
            assert key in obj.iterkeys()

    # Test itervalues() iteration

    with pytest.warns(DeprecationWarning):
        itervalues = list(obj.itervalues())
        assert len(itervalues) == 2
        for key in exp_dict:
            assert exp_dict[key] in itervalues

    # Test itervalues() containment

    with pytest.warns(DeprecationWarning):
        for key in exp_dict:
            assert exp_dict[key] in obj.itervalues()

    # Test iteritems() iteration

    with pytest.warns(DeprecationWarning):
        iteritems = list(obj.iteritems())
        assert len(iteritems) == 2
        for key in exp_dict:
            assert (key, exp_dict[key]) in iteritems

    # Test iteritems() containment

    with pytest.warns(DeprecationWarning):
        for key in exp_dict:
            assert (key, exp_dict[key]) in obj.iteritems()

    # Test in as test -> __getitem__()

    for key in exp_dict:
        assert key in obj

    # Test in as iteration -> __iter__()

    for key in obj:
        value = obj[key]
        assert value, exp_dict[key]

    # Test get()

    for key in exp_dict:
        assert obj.get(key) == exp_dict[key]
        assert obj.get(swapcase2(key)) == exp_dict[key]

    default_value = 'default_value'
    invalid_propname = 'undefined_Cheepy'
    value = obj.get(invalid_propname, default_value)
    assert value == default_value

    # Test update()

    obj.update({'One': '1', 'Two': '2'})
    assert obj['one'] == '1'
    assert obj['two'] == '2'
    for key in exp_dict:
        assert obj[key] == exp_dict[key]
    assert len(obj) == 4

    if version_info < (1, 0):
        # Updating from more than one positional arg has been removed in 1.0
        obj.update({'Three': '3', 'Four': '4'}, [('Five', '5')])
        assert obj['three'] == '3'
        assert obj['four'] == '4'
        assert obj['five'] == '5'
        assert len(obj) == 7
    else:
        obj.update({'Three': '3', 'Four': '4', 'Five': '5'})
        assert obj['three'] == '3'
        assert obj['four'] == '4'
        assert obj['five'] == '5'
        assert len(obj) == 7

    obj.update([('Six', '6')], Seven='7', Eight='8')
    assert obj['six'] == '6'
    assert obj['seven'] == '7'
    assert obj['eight'] == '8'
    assert len(obj) == 10

    obj.update(Nine='9', Ten='10')
    assert obj['nine'] == '9'
    assert obj['ten'] == '10'
    assert obj['Chicken'] == 'Ham'
    assert obj['Beans'] == 42
    assert len(obj) == 12

    del obj['one'], obj['two']
    del obj['three'], obj['four'], obj['five']
    del obj['six'], obj['seven'], obj['eight']
    del obj['nine'], obj['ten']
    assert len(obj) == 2


@pytest.mark.parametrize(
    "func_name",
    [
        '__lt__',
        '__gt__',
        '__ge__',
        '__le__',
    ]
)
@pytest.mark.parametrize(
    "objects",
    [
        (CIMInstance('Foo'), CIMInstance('Bar')),
        (CIMInstanceName('Foo'), CIMInstanceName('Bar')),
        (CIMClass('Foo'), CIMClass('Bar')),
        (CIMClassName('Foo'), CIMClassName('Bar')),
        (CIMProperty('Foo', 'a'), CIMProperty('Bar', 'b')),
        (CIMMethod('Foo', 'string'), CIMMethod('Bar', 'string')),
        (CIMParameter('Foo', 'string'), CIMParameter('Bar', 'string')),
        (CIMQualifier('Foo', 'a'), CIMQualifier('Bar', 'b')),
        (CIMQualifierDeclaration('Foo', 'string'),
         CIMQualifierDeclaration('Bar', 'string')),
    ]
)
@log_entry_exit
def test_object_comparison(objects, func_name):
    """
    Test function for comparison operators of CIM objects.
    The CIM objects intentionally do not implement comparison operators.
    """
    obj1, obj2 = objects
    func = getattr(obj1, func_name, None)
    assert func is not None
    # func is a bound method, i.e. self is already part of it and set to obj1
    with pytest.raises(TypeError) as exc_info:
        func(obj2)
    exc = exc_info.value
    assert "not supported between instances of" in str(exc)


TESTCASES_CIMINSTANCENAME_INIT = [

    # Testcases for CIMInstanceName.__init__()

    # 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 CIMInstanceName().
    #   * init_kwargs: Dict of keyword arguments to CIMInstanceName().
    #   * exp_attrs: Dict of expected attributes of resulting 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

    # Order of positional arguments
    (
        "Verify order of positional arguments",
        dict(
            init_args=[
                'CIM_Foo',
                dict(P1=True),
                'woot.com',
                'cimv2',
            ],
            init_kwargs={},
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(P1=True),
                host='woot.com',
                namespace='cimv2',
            ),
        ),
        None, None, True
    ),

    # Classname tests
    (
        "Verify that bytes classname is converted to unicode",
        dict(
            init_args=[],
            init_kwargs=dict(classname=b'CIM_Foo'),
            exp_attrs=dict(classname='CIM_Foo'),
        ),
        None, None, True
    ),
    (
        "Verify that unicode classname remains unicode",
        dict(
            init_args=[],
            init_kwargs=dict(classname='CIM_Foo'),
            exp_attrs=dict(classname='CIM_Foo'),
        ),
        None, None, True
    ),

    # Keybinding tests
    (
        "Verify that keybinding with name None succeeds (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(UNNAMED_KEY_NCD)),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=UNNAMED_KEY_NCD),
        ),
        None, None, True
    ),
    (
        "Verify keybindings order preservation with list of CIMProperty",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=[
                    CIMProperty('K1', value='Ham'),
                    CIMProperty('K2', value='Cheese'),
                ]
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict([
                    ('K1', 'Ham'),
                    ('K2', 'Cheese'),
                ])
            ),
        ),
        None, None, True
    ),
    (
        "Verify keybindings order preservation with OrderedDict",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=OrderedDict([
                    ('K1', 'Ham'),
                    ('K2', 'Cheese'),
                ])
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict([
                    ('K1', 'Ham'),
                    ('K2', 'Cheese'),
                ])
            ),
        ),
        None, None, True
    ),
    (
        "Verify keybindings order preservation with list of tuple(key,val)",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=[
                    ('K1', 'Ham'),
                    ('K2', 'Cheese'),
                ]
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict([
                    ('K1', 'Ham'),
                    ('K2', 'Cheese'),
                ])
            ),
        ),
        None, None, True
    ),
    (
        "Verify keybinding with bytes string value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=b'Ham')),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1='Ham')),
        ),
        None, None, True
    ),
    (
        "Verify keybinding with unicode string value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                # lower case a umlaut
                keybindings=dict(K1='H\u00E4m')),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1='H\u00E4m')),
        ),
        None, None, True
    ),
    (
        "Verify keybinding with boolean True value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=True)),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1=True)),
        ),
        None, None, True
    ),
    (
        "Verify keybinding with boolean False value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=False)),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1=False)),
        ),
        None, None, True
    ),
    (
        "Verify keybinding with int value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=42)),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1=42)),
        ),
        None, None, True
    ),
    (
        "Verify keybinding with Uint8 value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=Uint8(42))),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1=42)),
        ),
        None, None, True
    ),
    (
        "Verify keybinding with Uint16 value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=Uint16(4216))),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1=4216)),
        ),
        None, None, True
    ),
    (
        "Verify keybinding with Uint32 value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=Uint32(4232))),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1=4232)),
        ),
        None, None, True
    ),
    (
        "Verify keybinding with Uint64 value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=Uint64(4264))),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1=4264)),
        ),
        None, None, True
    ),
    (
        "Verify keybinding with Sint8 value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=Sint8(-42))),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1=-42)),
        ),
        None, None, True
    ),
    (
        "Verify keybinding with Sint16 value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=Sint16(-4216))),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1=-4216)),
        ),
        None, None, True
    ),
    (
        "Verify keybinding with Sint32 value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=Sint32(-4232))),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1=-4232)),
        ),
        None, None, True
    ),
    (
        "Verify keybinding with Sint64 value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=Sint64(-4264))),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1=-4264)),
        ),
        None, None, True
    ),
    (
        "Verify keybinding with float value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=42.1)),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1=42.1)),
        ),
        None, None, True
    ),
    (
        "Verify keybinding with Real32 value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=Real32(-42.32))),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1=-42.32)),
        ),
        None, None, True
    ),
    (
        "Verify keybinding with Real64 value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=Real64(-42.64))),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1=-42.64)),
        ),
        None, None, True
    ),
    (
        "Verify keybinding with datetime value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=DATETIME1_OBJ)),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1=DATETIME1_OBJ)),
        ),
        None, None, True
    ),
    (
        "Verify keybinding with timedelta value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=TIMEDELTA1_OBJ)),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1=TIMEDELTA1_OBJ)),
        ),
        None, None, True
    ),
    (
        "Verify keybinding with reference to instance",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=CIMINSTANCENAME_C1_OBJ)),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1=CIMINSTANCENAME_C1_OBJ)),
        ),
        None, None, True
    ),
    (
        "Verify keybinding with reference to class (fails)",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=CIMCLASSNAME_C1_OBJ)),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1=CIMCLASSNAME_C1_OBJ)),
        ),
        TypeError, None, True
    ),
    (
        "Verify keybinding with CIMProperty (of arbitrary type and value)",
        # Note: The full range of possible input values and types for
        # CIMProperty objects is tested in CIMProperty testcases.
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=CIMProperty('K1', value='Ham'))),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict([('K1', 'Ham')])),
        ),
        None, None, True
    ),
    (
        "Verify two keybindings",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=OrderedDict([('K1', 'Ham'), ('K2', Uint8(42))])),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict([('K1', 'Ham'), ('K2', Uint8(42))])),
        ),
        None, None, True
    ),
    (
        "Verify case insensitivity of keybinding names",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(Key1='Ham')),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict([('kEY1', 'Ham')])),
        ),
        None, None, True
    ),
    (
        "Verify that keybinding with None value fails with ValueError",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(Key1=None)),
            exp_attrs=None,
        ),
        ValueError, None, True
    ),

    # Namespace tests
    (
        "Verify that bytes namespace is converted to unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                namespace=b'root/cimv2'),
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2'),
        ),
        None, None, True
    ),
    (
        "Verify that unicode namespace remains unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2'),
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2'),
        ),
        None, None, True
    ),
    (
        "Verify that one leading and trailing slash in namespace get "
        "stripped",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                namespace='/root/cimv2/'),
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2'),
        ),
        None, None, True
    ),
    (
        "Verify that two leading and trailing slashes in namespace get "
        "stripped",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                namespace='//root/cimv2//'),
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2'),
        ),
        None, None, True
    ),

    # Host tests
    (
        "Verify that bytes host is converted to unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host=b'woot.com'),
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='woot.com'),
        ),
        None, None, True
    ),
    (
        "Verify that unicode host remains unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='woot.com'),
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='woot.com'),
        ),
        None, None, True
    ),

    # Exception testcases
    (
        "Verify that classname None fails",
        dict(
            init_args=[],
            init_kwargs=dict(classname=None),
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "Verify that keybindings with item of invalid type fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=[
                    'xxx_invalid_type'
                ]
            ),
            exp_attrs=None,
        ),
        TypeError, None, True
    ),
    (
        "Verify that keybinding with inconsistent name fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=CIMProperty('K1_X', value='Ham'))),
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "Verify that keybinding with a value of an embedded class fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=CIMClass('CIM_EmbClass'))),
            exp_attrs=None,
        ),
        TypeError, None, True
    ),
    (
        "Verify that keybinding with a value of an embedded instance fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=CIMInstance('CIM_EmbInst'))),
            exp_attrs=None,
        ),
        TypeError, None, True
    ),
    (
        "Verify that keybinding with an array value fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=[1, 2])),
            exp_attrs=None,
        ),
        TypeError, None, True
    ),
    (
        "Verify that keybinding with a value of some other unsupported "
        "object type (e.g. exception type) fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(K1=TypeError)),
            exp_attrs=None,
        ),
        TypeError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCENAME_INIT)
@simplified_test_function
@log_entry_exit
def test_CIMInstanceName_init(testcase, init_args, init_kwargs, exp_attrs):
    """
    Test function for CIMInstanceName.__init__()
    """

    # The code to be tested
    obj = CIMInstanceName(*init_args, **init_kwargs)

    assert not hasattr(obj, '__dict__')

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

    exp_classname = exp_attrs['classname']
    assert obj.classname == exp_classname
    assert isinstance(obj.classname, type(exp_classname))

    exp_keybindings = exp_attrs.get('keybindings', NocaseDict())
    assert obj.keybindings == exp_keybindings
    assert isinstance(obj.keybindings, type(exp_keybindings))

    exp_host = exp_attrs.get('host', None)
    assert obj.host == exp_host
    assert isinstance(obj.host, type(exp_host))

    exp_namespace = exp_attrs.get('namespace', None)
    assert obj.namespace == exp_namespace
    assert isinstance(obj.namespace, type(exp_namespace))


TESTCASES_KEYBINDING_CONFIG = [

    # Testcases for CIMInstanceName.__init__()

    # 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 CIMInstanceName().
    #   * ignore_flag: Value of IGNORE_NULL_KEY_VALUE config variable
    #   * init_kwargs: Dict of keyword arguments to CIMInstanceName().
    #   * exp_attrs: Dict of expected attributes of resulting 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
    (
        "Verify that keybinding with None value fails with ValueError when"
        "config variable False",
        dict(
            init_args=[],
            ignore_flag=False,
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(Key1=None)),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=dict(Key1=None)),
        ),
        ValueError, None, True
    ),
    (
        "Verify that keybinding with None value OK with config True",
        dict(
            init_args=[],
            ignore_flag=True,
            init_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(Key1=None)),
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=dict(Key1=None)),
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_KEYBINDING_CONFIG)
@simplified_test_function
@log_entry_exit
def test_keybinding_config_option(
        testcase, init_args, ignore_flag, init_kwargs, exp_attrs):
    """
    Test CIMInstanceName behavior with changes to config variable
    IGNORE_NULL_KEY_VALUE.
    """
    # pylint: disable=unused-variable
    # patch with context resets original at end of test
    with patch('pywbem.config.IGNORE_NULL_KEY_VALUE', ignore_flag):

        # The code to be tested
        obj = CIMInstanceName(*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

        exp_classname = exp_attrs['classname']
        assert obj.classname == exp_classname
        assert isinstance(obj.classname, type(exp_classname))

        exp_keybindings = exp_attrs.get('keybindings', NocaseDict())
        assert obj.keybindings == exp_keybindings


TESTCASES_CIMINSTANCENAME_COPY = [

    # Testcases for CIMInstanceName.copy()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj_kwargs: Dict of init kwargs for original CIMInstanceName.
    # * 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

    (
        "All attributes set",
        dict(
            obj_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(P1=True),
                host='woot.com',
                namespace='cimv2',
            )
        ),
        None, None, True
    ),
    (
        "No Keybindings",
        dict(
            obj_kwargs=dict(
                classname='CIM_Foo',
                keybindings=None,
                host='woot.com',
                namespace='cimv2',
            )
        ),
        None, None, True
    ),
    (
        "No Host",
        dict(
            obj_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(P1=True),
                host=None,
                namespace='cimv2',
            )
        ),
        None, None, True
    ),
    (
        "No Namespace",
        dict(
            obj_kwargs=dict(
                classname='CIM_Foo',
                keybindings=dict(P1=True),
                host='woot.com',
                namespace=None,
            )
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCENAME_COPY)
@simplified_test_function
@log_entry_exit
def test_CIMInstanceName_copy(testcase, obj_kwargs):
    """
    Test function for CIMInstanceName.copy()
    """

    obj1 = CIMInstanceName(**obj_kwargs)

    # The code to be tested
    obj2 = obj1.copy()

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

    # Verify that the copy is equal to the original (performs deep equality)
    assert obj2 == obj1

    # Verify that the copy is a different object
    assert id(obj2) != id(obj1)

    # Verify that the mutable child objects are different objects
    if obj1.keybindings is not None:
        assert id(obj2.keybindings) != id(obj1.keybindings)

    # Verify that the copy can be modified and the original remains unchanged

    obj1_classname = obj1.classname
    obj2.classname = 'SomeNewClassname'
    assert obj1.classname == obj1_classname

    obj1_keybindings = obj1.keybindings
    obj2.keybindings = NocaseDict([('SomeNewKey', '5678')])
    assert obj1.keybindings == obj1_keybindings

    obj1_host = obj1.host
    obj2.host = 'SomeNewHost'
    assert obj1.host == obj1_host

    obj1_namespace = obj1.namespace
    obj2.namespace = 'SomeNewNamespace'
    assert obj1.namespace == obj1_namespace


CIMINSTANCENAME_SETATTR_C1_KWARGS = dict(
    classname='C1',
    keybindings=dict(P1=True),
    host='woot.com',
    namespace='cimv2',
)

TESTCASES_CIMINSTANCENAME_SETATTR = [

    # Testcases for CIMInstanceName set attribute

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj_kwargs: Dict of init kwargs for CIMInstanceName.
    #   * item: Name of CIMInstanceName attr to set, or tuple
    #     (item, key) to set a single item in an attr that is a dict.
    #   * new_value: New value to set.
    #   * exp_attrs: Dict of expected attributes, for validation.
    # * 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

    # Tests that set the classname attribute
    (
        "Set classname to different string",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item='classname',
            new_value='CIM_Bar',
            exp_attrs=dict(
                classname='CIM_Bar',
            ),
        ),
        None, None, True
    ),
    (
        "Set classname to None",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item='classname',
            new_value=None,
            exp_attrs=dict(
                classname=None,
            ),
        ),
        # raises ValueError since 0.12
        ValueError, None, True
    ),

    # Tests that set the keybindings attribute
    (
        "Set keybindings to new dict with value (boolean)",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item='keybindings',
            new_value=dict(P2=True),
            exp_attrs=dict(
                keybindings=dict(P2=True),
            ),
        ),
        None, None, True
    ),
    (
        "Set keybindings to new dict with CIMProperty with correct name",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item='keybindings',
            new_value=dict(P2=CIMProperty('P2', True)),
            exp_attrs=dict(
                keybindings=dict(P2=True),
            ),
        ),
        None, None, True
    ),
    (
        "Set keybindings to new dict with CIMProperty with incorrect name",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item='keybindings',
            new_value=dict(P2=CIMProperty('P2x', True)),
            exp_attrs=dict(
                keybindings=None,
            ),
        ),
        # raises ValueError since 0.12
        ValueError, None, True
    ),
    (
        "Set keybindings to new dict with CIMProperty with name None",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item='keybindings',
            # Name of CIMProperty object is not None, to get over that check.
            new_value=dict([(None, CIMProperty('Pnone', True))]),
            exp_attrs=dict(
                keybindings=dict([
                    ('P1', True),
                    (None, True),
                ]),
            ),
        ),
        # raises TypeError since 0.12
        TypeError, None, True
        # CIMProperty only allowd when key not None
    ),
    (
        "Set keybindings to new dict with integer",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item='keybindings',
            new_value=dict(KeyInt=42),
            exp_attrs=dict(
                keybindings=dict(KeyInt=42),
            ),
        ),
        None, None, True
    ),
    (
        "Set keybindings to new dict with float",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item='keybindings',
            new_value=dict(KeyFloat=7.5),
            exp_attrs=dict(
                keybindings=dict(KeyFloat=7.5),
            ),
        ),
        None, None, True
    ),
    (
        "Set keybindings to new dict with key None",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item='keybindings',
            new_value={None: 'bar'},
            exp_attrs=dict(
                # NocaseDict does not allow None as key, so we use dict:
                keybindings=dict([(None, 'bar')]),
            ),
        ),
        None, None, True
    ),
    (
        "Set keybindings to None",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item='keybindings',
            new_value=None,
            exp_attrs=dict(
                keybindings={},
            ),
        ),
        None, None, True
    ),
    (
        "Set existing keybinding item to new value (boolean)",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item=('keybindings', 'P1'),
            new_value=False,
            exp_attrs=dict(
                keybindings=dict(P1=False),
            ),
        ),
        None, None, True
    ),
    (
        "Set existing keybinding item to new CIMProperty with correct name",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item=('keybindings', 'P1'),
            new_value=CIMProperty('P1', True),
            # No conversion to CIMProperty.value since this sets an item in a
            # normal NocaseDict:
            exp_attrs=dict(
                keybindings=dict(P1=CIMProperty('P1', True)),
            ),
        ),
        None, None, True
    ),
    (
        "Set existing keybinding item to new CIMProperty with incorrect name",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item=('keybindings', 'P1'),
            new_value=CIMProperty('P1x', True),
            # No checking for correct name since this sets an item in a normal
            # NocaseDict:
            exp_attrs=dict(
                keybindings=dict(P1=CIMProperty('P1x', True)),
            ),
        ),
        None, None, True
    ),
    (
        "Set existing keybinding item to new integer",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item=('keybindings', 'P1'),
            new_value=42,
            exp_attrs=dict(
                keybindings=dict(P1=42),
            ),
        ),
        None, None, True
    ),
    (
        "Set existing keybinding item to new float",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item=('keybindings', 'P1'),
            new_value=7.5,
            exp_attrs=dict(
                keybindings=dict(P1=7.5),
            ),
        ),
        None, None, True
    ),
    (
        "Set existing keybinding item to None",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item=('keybindings', 'P1'),
            new_value=None,
            exp_attrs=dict(
                keybindings=dict(P1=None),
            ),
        ),
        None, None, True
    ),
    (
        "Set new keybinding item to new value (boolean)",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item=('keybindings', 'P2'),
            new_value=False,
            exp_attrs=dict(
                keybindings=dict(P1=True, P2=False),
            ),
        ),
        None, None, True
    ),
    (
        "Set new keybinding item to new CIMProperty with correct name",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item=('keybindings', 'P2'),
            new_value=CIMProperty('P2', True),
            # No conversion to CIMProperty.value since this sets an item in a
            # normal NocaseDict:
            exp_attrs=dict(
                keybindings=dict(P1=True, P2=CIMProperty('P2', True)),
            ),
        ),
        None, None, True
    ),
    (
        "Set new keybinding item to new CIMProperty with incorrect name",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item=('keybindings', 'P2'),
            new_value=CIMProperty('P2x', True),
            # No checking for correct name since this sets an item in a normal
            # NocaseDict:
            exp_attrs=dict(
                keybindings=dict(P1=True, P2=CIMProperty('P2x', True)),
            ),
        ),
        None, None, True
    ),
    (
        "Set new keybinding item with name None",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item=('keybindings', None),
            new_value=CIMProperty('Pnone', True),
            # No conversion to CIMProperty.value since this sets an item in a
            # normal NocaseDict:
            exp_attrs=dict(
                keybindings=dict([
                    ('P1', True),
                    (None, CIMProperty('Pnone', True)),
                ]),
            ),
        ),
        None, None, True  # None as key in NocaseDict
    ),
    (
        "Set new keybinding item to new integer",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item=('keybindings', 'P2'),
            new_value=42,
            exp_attrs=dict(
                keybindings=dict(P1=True, P2=42),
            ),
        ),
        None, None, True
    ),
    (
        "Set new keybinding item to new float",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item=('keybindings', 'P2'),
            new_value=7.5,
            exp_attrs=dict(
                keybindings=dict(P1=True, P2=7.5),
            ),
        ),
        None, None, True
    ),
    (
        "Set new keybinding item to None",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item=('keybindings', 'P2'),
            new_value=None,
            exp_attrs=dict(
                keybindings=dict(P1=True, P2=None),
            ),
        ),
        None, None, True
    ),
    (
        "Set new keybinding item with key None (to string)",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item=('keybindings', None),
            new_value='foo',
            exp_attrs=dict(
                # NocaseDict does not allow None as key, so we use dict:
                keybindings=dict([('P1', True), (None, 'foo')]),
            ),
        ),
        None, None, True
    ),

    # Tests that set the host attribute
    (
        "Set host to different string",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item='host',
            new_value='foo',
            exp_attrs=dict(
                host='foo',
            ),
        ),
        None, None, True
    ),
    (
        "Set host to None",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item='host',
            new_value=None,
            exp_attrs=dict(
                host=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the namespace attribute
    (
        "Set namespace to different string",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item='namespace',
            new_value='foo',
            exp_attrs=dict(
                namespace='foo',
            ),
        ),
        None, None, True
    ),
    (
        "Set namespace to None",
        dict(
            obj_kwargs=CIMINSTANCENAME_SETATTR_C1_KWARGS,
            item='namespace',
            new_value=None,
            exp_attrs=dict(
                namespace=None,
            ),
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCENAME_SETATTR)
@simplified_test_function
@log_entry_exit
def test_CIMInstanceName_setattr(
        testcase, obj_kwargs, item, new_value, exp_attrs):
    """
    Test function for CIMInstanceName set attribute
    """

    obj = CIMInstanceName(**obj_kwargs)

    if isinstance(item, tuple):
        attr_name, attr_key = item
        attr_dict = getattr(obj, attr_name)

        # The code to be tested (for setting a single dict item)
        attr_dict[attr_key] = new_value

    else:
        attr_name = item

        # The code to be tested (for setting the entire attribute)
        setattr(obj, attr_name, new_value)

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

    for attr_name in exp_attrs:
        exp_value = exp_attrs[attr_name]
        value = getattr(obj, attr_name)
        assert value == exp_value


@log_entry_exit
def test_CIMInstanceName_eq_keybindings_order():
    """
    Test that two CIMInstanceName objects compare equal if their key
    bindings have a different order.

    This test was motivated by pywbem issue #686.

    This test attempts to construct dictionaries that have a different
    iteration order for their items. This may not work on all Python
    implementations and versions, but having the same order does not
    invalidate this test, it just lowers the quality of this test.

    The approach to achieve different iteration orders is based on the
    knowledge that in CPython<=3.2, the dict implementation uses a hash
    table with an initial size of 8 (which doubles its size under certain
    conditions). The two test keys 'bar' and 'baz' happen to have the
    same hash value of 4 (= hash(key) % 8) , and therefore occupy the
    same slot in the hash table (i.e. they produce a hash collision).
    In such a case, the iteration order depends on the order in which the
    items were added to the dictionary. For details, read
    https://stackoverflow.com/a/15479974/1424462.
    """

    key1 = 'bar'
    key2 = 'baz'  # should have same hash value as key1 (= hash(key) % 8)
    value1 = 'a'
    value2 = 'b'

    d1 = OrderedDict([(key1, value1), (key2, value2)])
    kb1 = NocaseDict(d1)
    obj1 = CIMInstanceName('CIM_Foo', kb1)

    d2 = OrderedDict([(key2, value2), (key1, value1)])
    kb2 = NocaseDict(d2)
    obj2 = CIMInstanceName('CIM_Foo', kb2)

    for k in obj1.keybindings:
        k1_first = k
        break
    for k in obj2.keybindings:
        k2_first = k
        break
    if k1_first != k2_first:
        # The key bindings do have different iteration order, so we have
        # a high test quality.
        pass
    else:
        print("\nInfo: CIMInstanceNameEquality.test_keybindings_order(): "
              "Key bindings have the same order of keys, lowering the "
              "quality of this test.")
        print(f"  Hash values of keys: k1={key1!r} (hash: {hash(key1) % 8}), "
              f"k2={key2!r} (hash: {hash(key2) % 8})")
        print(f"  First keys: k1={k1_first!r}, k2={k2_first!r}")
        print(f"  Input dicts: d1={d1!r}, d2={d2!r}")
        print(f"  Input key bindings: kb1={kb1!r}, kb2={kb2!r}")
        print(f"  Object key bindings: obj1.kb={obj1.keybindings!r}, "
              f"obj2.kb={obj2.keybindings!r}")
        print(f"  Objects:\n    obj1={obj1!r}\n    obj2={obj2!r}")
    assert obj1 == obj2, (
        "CIMInstanceName objects with different iteration order of "
        "key bindings do not compare equal:\n"
        f"  obj1={obj1}\n"
        f"  obj2={obj2}")


TESTCASES_CIMINSTANCENAME_HASH_EQ = [

    # Testcases for CIMInstanceName.__hash__() and __eq__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: CIMInstanceName object #1 to be tested.
    #   * obj2: CIMInstanceName object #2 to be tested.
    #   * exp_equal: Expected equality of the object hash values.
    # * 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

    # Classname tests
    (
        "Classname, equal with same lexical case",
        dict(
            obj1=CIMInstanceName('CIM_Foo'),
            obj2=CIMInstanceName('CIM_Foo'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Classname, equal with different lexical case",
        dict(
            obj1=CIMInstanceName('CIM_Foo'),
            obj2=CIMInstanceName('ciM_foO'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Classname, different",
        dict(
            obj1=CIMInstanceName('CIM_Foo'),
            obj2=CIMInstanceName('CIM_Foo_x'),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Host tests
    (
        "Host name, equal with same lexical case",
        dict(
            obj1=CIMInstanceName('CIM_Foo', host='woot.com'),
            obj2=CIMInstanceName('CIM_Foo', host='woot.Com'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Host name, equal with different lexical case",
        dict(
            obj1=CIMInstanceName('CIM_Foo', host='woot.com'),
            obj2=CIMInstanceName('CIM_Foo', host='Woot.Com'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Host name, different with None / string",
        dict(
            obj1=CIMInstanceName('CIM_Foo', host=None),
            obj2=CIMInstanceName('CIM_Foo', host='woot.com'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Host name, different with string / None",
        dict(
            obj1=CIMInstanceName('CIM_Foo', host='woot.com'),
            obj2=CIMInstanceName('CIM_Foo', host=None),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Host name, equal with None / None",
        dict(
            obj1=CIMInstanceName('CIM_Foo', host=None),
            obj2=CIMInstanceName('CIM_Foo', host=None),
            exp_equal=True,
        ),
        None, None, True
    ),

    # Namespace tests
    (
        "Namespace, equal with same lexical case",
        dict(
            obj1=CIMInstanceName('CIM_Foo', namespace='root/cimv2'),
            obj2=CIMInstanceName('CIM_Foo', namespace='root/cimv2'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Namespace, equal with different lexical case",
        dict(
            obj1=CIMInstanceName('CIM_Foo', namespace='root/cimv2'),
            obj2=CIMInstanceName('CIM_Foo', namespace='Root/CIMv2'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Namespace, different",
        dict(
            obj1=CIMInstanceName('CIM_Foo', namespace='root/cimv2'),
            obj2=CIMInstanceName('CIM_Foo', namespace='abc'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Namespace, different with None / string",
        dict(
            obj1=CIMInstanceName('CIM_Foo', namespace=None),
            obj2=CIMInstanceName('CIM_Foo', namespace='root/cimv2'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Namespace, different with string / None",
        dict(
            obj1=CIMInstanceName('CIM_Foo', namespace='root/cimv2'),
            obj2=CIMInstanceName('CIM_Foo', namespace=None),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Namespace, equal with None / None",
        dict(
            obj1=CIMInstanceName('CIM_Foo', namespace=None),
            obj2=CIMInstanceName('CIM_Foo', namespace=None),
            exp_equal=True,
        ),
        None, None, True
    ),

    # Keybindings tests
    (
        "Matching keybindings, both None",
        dict(
            obj1=CIMInstanceName('CIM_Foo', keybindings=None),
            obj2=CIMInstanceName('CIM_Foo', keybindings=None),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Matching keybindings, one None, one empty dict",
        dict(
            obj1=CIMInstanceName('CIM_Foo', keybindings=None),
            obj2=CIMInstanceName('CIM_Foo', keybindings={}),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Non-matching keybindings, first one None and second one not None",
        dict(
            obj1=CIMInstanceName('CIM_Foo', keybindings=None),
            obj2=CIMInstanceName('CIM_Foo', keybindings={'Cheepy': 'Birds'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching keybindings, first one not None and second one None",
        dict(
            obj1=CIMInstanceName('CIM_Foo', keybindings={'Cheepy': 'Birds'}),
            obj2=CIMInstanceName('CIM_Foo', keybindings=None),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Matching keybindings, key names with same lexical case",
        dict(
            obj1=CIMInstanceName('CIM_Foo', keybindings={'Cheepy': 'Birds'}),
            obj2=CIMInstanceName('CIM_Foo', keybindings={'Cheepy': 'Birds'}),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Matching keybindings, key names with different lexical case",
        dict(
            obj1=CIMInstanceName('CIM_Foo', keybindings={'Cheepy': 'Birds'}),
            obj2=CIMInstanceName('CIM_Foo', keybindings={'cheepy': 'Birds'}),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Non-matching keybindings, one keybinding more",
        dict(
            obj1=CIMInstanceName('CIM_Foo'),
            obj2=CIMInstanceName('CIM_Foo', keybindings={'Cheepy': 'Birds'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching keybindings, one keybinding less",
        dict(
            obj1=CIMInstanceName('CIM_Foo', keybindings={'Cheepy': 'Birds'}),
            obj2=CIMInstanceName('CIM_Foo'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching keybindings, different keybindings",
        dict(
            obj1=CIMInstanceName('CIM_Foo', keybindings={'Cheepy': 'Birds'}),
            obj2=CIMInstanceName('CIM_Foo', keybindings={'Creepy': 'Ants'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Matching keybindings, with values that differ in lexical case",
        dict(
            obj1=CIMInstanceName('CIM_Foo', keybindings={'Cheepy': 'Birds'}),
            obj2=CIMInstanceName('CIM_Foo', keybindings={'Cheepy': 'birds'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Matching keybindings, with values that is unicode / string",
        dict(
            obj1=CIMInstanceName('CIM_Foo', keybindings={'Cheepy': 'Birds'}),
            obj2=CIMInstanceName('CIM_Foo', keybindings={'Cheepy': 'Birds'}),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Equal keybindings with a number of types",
        dict(
            obj1=CIMInstanceName('CIM_Foo', keybindings={
                'Name': 'Foo',
                'Boolean': False,
                'Number': Uint8(42),
                'Ref': CIMInstanceName('CIM_Bar'),
            }),
            obj2=CIMInstanceName('CIM_Foo', keybindings={
                'Name': 'Foo',
                'Boolean': False,
                'Number': Uint8(42),
                'Ref': CIMInstanceName('CIM_Bar'),
            }),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Keybinding with different types (on input!): int / Uint8",
        dict(
            obj1=CIMInstanceName('CIM_Foo', keybindings={'Foo': 42}),
            obj2=CIMInstanceName('CIM_Foo', keybindings={'Foo': Uint8(42)}),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Keybinding with different types: bool True / string 'TRUE'",
        dict(
            obj1=CIMInstanceName('CIM_Foo', keybindings={'Foo': True}),
            obj2=CIMInstanceName('CIM_Foo', keybindings={'Foo': 'TRUE'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Keybinding with different types: bool False / string 'FALSE'",
        dict(
            obj1=CIMInstanceName('CIM_Foo', keybindings={'Foo': False}),
            obj2=CIMInstanceName('CIM_Foo', keybindings={'Foo': 'FALSE'}),
            exp_equal=False,
        ),
        None, None, True
    ),
]

TESTCASES_CIMINSTANCENAME_EQ = [

    # Additional testcases for CIMInstanceName.__eq__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: CIMInstanceName object #1 to be tested.
    #   * obj2: CIMInstanceName object #2 to be tested.
    #   * exp_equal: Expected equality of the object hash values.
    # * 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

    # Exception testcases
    (
        "Invalid type of second object: string",
        dict(
            obj1=CIMInstanceName('CIM_Foo'),
            obj2='abc',
            exp_equal=None,
        ),
        TypeError, None, True
    ),
    (
        "Invalid type of second object: URI string",
        dict(
            obj1=CIMInstanceName('CIM_Foo'),
            obj2='http://abc:CIM_Foo',
            exp_equal=None,
        ),
        TypeError, None, True
    ),
    (
        "Invalid type of second object: CIMInstance",
        dict(
            obj1=CIMInstanceName('CIM_Foo'),
            obj2=CIMInstance('CIM_Foo'),
            exp_equal=None,
        ),
        TypeError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCENAME_HASH_EQ)
@simplified_test_function
@log_entry_exit
def test_CIMInstanceName_hash(testcase, obj1, obj2, exp_equal):
    """
    Test function for CIMInstanceName.__hash__().
    """

    # The code to be tested
    hash1 = hash(obj1)
    hash2 = hash(obj2)

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

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

    if exp_equal:
        assert hash1 == hash2
    else:
        assert hash1 != hash2


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCENAME_HASH_EQ + TESTCASES_CIMINSTANCENAME_EQ)
@simplified_test_function
@log_entry_exit
def test_CIMInstanceName_eq(testcase, obj1, obj2, exp_equal):
    """
    Test function for CIMInstanceName.__eq__().
    """

    # The code to be tested
    equal = (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

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

    assert equal == exp_equal


TESTCASES_CIMINSTANCENAME_REPR = [

    # Testcases for CIMInstanceName.__repr__() / repr()
    # Note: CIMInstanceName.__str__() is tested along with to_wbem_uri()

    # Each list item is a testcase tuple with these items:
    # * obj: CIMInstanceName object to be tested.

    (
        CIMInstanceName(
            classname='CIM_Foo',
            keybindings=None)
    ),
    (
        CIMInstanceName(
            classname='CIM_Foo',
            keybindings={})
    ),
    # (
    #    CIMInstanceName(
    #        classname='CIM_Foo',
    #        keybindings=dict(InstanceID=None))
    # ),
    (
        CIMInstanceName(
            classname='CIM_Foo',
            keybindings=dict(InstanceID='1234'))
    ),
    (
        CIMInstanceName(
            classname='CIM_Foo',
            keybindings=dict(InstanceID='1234'),
            namespace='cimv2')
    ),
    (
        CIMInstanceName(
            classname='CIM_Foo',
            keybindings=dict(InstanceID='1234'),
            namespace='root/cimv2',
            host='10.11.12.13:5989')
    ),
    (
        CIMInstanceName(
            classname='CIM_Foo',
            keybindings=dict(InstanceID='1234'),
            namespace='root/cimv2',
            host='jdd:test@10.11.12.13')
    ),
]


@pytest.mark.parametrize(
    "obj",
    TESTCASES_CIMINSTANCENAME_REPR)
@log_entry_exit
def test_CIMInstanceName_repr(obj):
    """
    Test function for CIMInstanceName.__repr__() / repr()

    Note: CIMInstanceName.__str__() is tested along with to_wbem_uri()
    """

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

    assert re.match(r'^CIMInstanceName\(', r)

    exp_classname = _format('classname={0!A}', obj.classname)
    assert exp_classname in r

    assert 'keybindings=' in r
    if obj.keybindings:
        for key, value in obj.keybindings.items():
            search_str = _format("{0!A}: {1!A}", key, value)
            assert re.search(search_str, r), f"For key {key}"

    exp_namespace = _format('namespace={0!A}', obj.namespace)
    assert exp_namespace in r

    exp_host = _format('host={0!A}', obj.host)
    assert exp_host in r


# Some CIMInstanceName objects for CIMInstanceName.tocimxml() tests

CIMINSTANCENAME_INV_KEYBINDINGS_1 = CIMInstanceName(
    'CIM_Foo', keybindings=dict(Name='Foo'))
CIMINSTANCENAME_INV_KEYBINDINGS_1.keybindings['Foo'] = datetime(2017, 1, 1)

CIMINSTANCENAME_INV_KEYBINDINGS_2 = CIMInstanceName(
    'CIM_Foo', keybindings=dict(Name='Foo'))
CIMINSTANCENAME_INV_KEYBINDINGS_2.keybindings['Foo'] = CIMParameter(
    'Foo', type='string')

CIMINSTANCENAME_INV_KEYBINDINGS_3 = CIMInstanceName(
    'CIM_Foo', keybindings=dict(Name='Foo'))
CIMINSTANCENAME_INV_KEYBINDINGS_3.keybindings['Foo'] = CIMProperty(
    'Foo', type='string', value='bla')

TESTCASES_CIMINSTANCENAME_TOCIMXML = [

    # Testcases for CIMInstanceName.tocimxml() and tocimxmlstr()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: CIMInstanceName object to be tested.
    #   * kwargs: Dict of input args for tocimxml().
    #   * exp_xml_str: Expected CIM-XML string, as a tuple/list of parts.
    # * 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

    # Classname-only tests
    (
        "Classname only, with implied default args",
        dict(
            obj=CIMInstanceName('CIM_Foo'),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo"/>',
            )
        ),
        None, MissingKeybindingsWarning, True
    ),
    (
        "Classname only, with specified default args",
        dict(
            obj=CIMInstanceName('CIM_Foo'),
            kwargs=dict(
                ignore_host=False,
                ignore_namespace=False,
            ),
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo"/>',
            )
        ),
        None, MissingKeybindingsWarning, True
    ),
    (
        "Classname only, with non-default args: ignore_host=True",
        dict(
            obj=CIMInstanceName('CIM_Foo'),
            kwargs=dict(
                ignore_host=True,
            ),
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo"/>',
            )
        ),
        None, MissingKeybindingsWarning, True
    ),
    (
        "Classname only, with non-default args: ignore_namespace=True",
        dict(
            obj=CIMInstanceName('CIM_Foo'),
            kwargs=dict(
                ignore_namespace=True,
            ),
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo"/>',
            )
        ),
        None, MissingKeybindingsWarning, True
    ),

    # Classname with one keybinding tests
    (
        "Classname with one keybinding with value of CIMProperty string",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                [CIMProperty('Cheepy', 'Birds', type='string')]
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="string" TYPE="string">Birds</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of CIMProperty char16",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                [CIMProperty('Cheepy', 'Birds', type='char16')]
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="string" TYPE="char16">Birds</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of CIMProperty boolean",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                [CIMProperty('Cheepy', True, type='boolean')]
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="boolean" TYPE="boolean">TRUE</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of CIMProperty uint8",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                [CIMProperty('Cheepy', 42, type='uint8')]
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="numeric" TYPE="uint8">42</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of CIMProperty uint16",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                [CIMProperty('Cheepy', 42, type='uint16')]
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="numeric" TYPE="uint16">42</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of CIMProperty uint32",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                [CIMProperty('Cheepy', 42, type='uint32')]
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="numeric" TYPE="uint32">42</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of CIMProperty uint64",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                [CIMProperty('Cheepy', 42, type='uint64')]
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="numeric" TYPE="uint64">42</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of CIMProperty sint8",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                [CIMProperty('Cheepy', 42, type='sint8')]
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="numeric" TYPE="sint8">42</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of CIMProperty sint16",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                [CIMProperty('Cheepy', 42, type='sint16')]
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="numeric" TYPE="sint16">42</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of CIMProperty sint32",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                [CIMProperty('Cheepy', 42, type='sint32')]
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="numeric" TYPE="sint32">42</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of CIMProperty sint64",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                [CIMProperty('Cheepy', 42, type='sint64')]
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="numeric" TYPE="sint64">42</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of CIMProperty real32",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                [CIMProperty('Cheepy', 42.1, type='real32')]
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="numeric" TYPE="real32">42.1</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of CIMProperty real64",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                [CIMProperty('Cheepy', 42.1, type='real64')]
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="numeric" TYPE="real64">42.1</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of CIMProperty datetime",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                [CIMProperty('Cheepy',
                             CIMDateTime('19980125133015.123456-300'),
                             type='datetime')]
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="string" TYPE="datetime">'
                '19980125133015.123456-300</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of CIMProperty reference",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                [CIMProperty('Cheepy', CIMInstanceName('CIM_Ref'),
                             type='reference')]
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<VALUE.REFERENCE>',
                '<INSTANCENAME CLASSNAME="CIM_Ref"/>',
                '</VALUE.REFERENCE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, MissingKeybindingsWarning, True  # for the inner ref
    ),
    (
        "Classname with one keybinding with value of Char16 with unicode value",
        dict(
            obj=CIMInstanceName('CIM_Foo', {'Cheepy': Char16('Birds')}),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="string" TYPE="char16">Birds</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of Char16 with byte value",
        dict(
            obj=CIMInstanceName('CIM_Foo', {'Cheepy': Char16(b'Birds')}),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="string" TYPE="char16">Birds</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of unicode string type",
        dict(
            obj=CIMInstanceName('CIM_Foo', {'Cheepy': 'Birds'}),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="string" TYPE="string">Birds</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of byte string type",
        dict(
            obj=CIMInstanceName('CIM_Foo', {'Cheepy': b'Birds'}),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="string" TYPE="string">Birds</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of boolean True",
        dict(
            obj=CIMInstanceName('CIM_Foo', {'Cheepy': True}),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="boolean" TYPE="boolean">TRUE</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of boolean False",
        dict(
            obj=CIMInstanceName('CIM_Foo', {'Cheepy': False}),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="boolean" TYPE="boolean">FALSE</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of int",
        dict(
            obj=CIMInstanceName('CIM_Foo', {'Cheepy': 42}),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="numeric">42</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of Uint8",
        dict(
            obj=CIMInstanceName('CIM_Foo', {'Cheepy': Uint8(42)}),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="numeric" TYPE="uint8">42</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of Uint16",
        dict(
            obj=CIMInstanceName('CIM_Foo', {'Cheepy': Uint16(42)}),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="numeric" TYPE="uint16">42</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of Uint32",
        dict(
            obj=CIMInstanceName('CIM_Foo', {'Cheepy': Uint32(42)}),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="numeric" TYPE="uint32">42</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of Uint64",
        dict(
            obj=CIMInstanceName('CIM_Foo', {'Cheepy': Uint64(42)}),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="numeric" TYPE="uint64">42</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of Sint8",
        dict(
            obj=CIMInstanceName('CIM_Foo', {'Cheepy': Sint8(42)}),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="numeric" TYPE="sint8">42</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of Sint16",
        dict(
            obj=CIMInstanceName('CIM_Foo', {'Cheepy': Sint16(42)}),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="numeric" TYPE="sint16">42</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of Sint32",
        dict(
            obj=CIMInstanceName('CIM_Foo', {'Cheepy': Sint32(42)}),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="numeric" TYPE="sint32">42</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of Sint64",
        dict(
            obj=CIMInstanceName('CIM_Foo', {'Cheepy': Sint64(42)}),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="numeric" TYPE="sint64">42</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of float",
        dict(
            obj=CIMInstanceName('CIM_Foo', {'Cheepy': 42.1}),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="numeric">42.1</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of Real32",
        dict(
            obj=CIMInstanceName('CIM_Foo', {'Cheepy': Real32(42.1)}),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="numeric" TYPE="real32">42.1</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of Real64",
        dict(
            obj=CIMInstanceName('CIM_Foo', {'Cheepy': Real64(42.1)}),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="numeric" TYPE="real64">42.1</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of CIMDateTime",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                {'Cheepy': CIMDateTime('19980125133015.123456-300')}
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="string" TYPE="datetime">'
                '19980125133015.123456-300</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding with value of CIMInstanceName without "
        "host/namespace",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                {'Cheepy': CIMInstanceName('CIM_Ref')}
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<VALUE.REFERENCE>',
                '<INSTANCENAME CLASSNAME="CIM_Ref"/>',
                '</VALUE.REFERENCE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, MissingKeybindingsWarning, True  # for the inner ref
    ),
    (
        "Classname with one keybinding with value of CIMInstanceName without "
        "host but with namespace",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                {'Cheepy': CIMInstanceName('CIM_Ref', namespace='root/cimv2')}
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<VALUE.REFERENCE>',
                '<LOCALINSTANCEPATH>',
                '<LOCALNAMESPACEPATH>',
                '<NAMESPACE NAME="root"/>',
                '<NAMESPACE NAME="cimv2"/>',
                '</LOCALNAMESPACEPATH>',
                '<INSTANCENAME CLASSNAME="CIM_Ref"/>',
                '</LOCALINSTANCEPATH>',
                '</VALUE.REFERENCE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, MissingKeybindingsWarning, True  # for the inner ref
    ),
    (
        "Classname with one keybinding with value of CIMInstanceName with "
        "host and namespace",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                {'Cheepy': CIMInstanceName('CIM_Ref', namespace='root/cimv2',
                                           host='woot')}
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<VALUE.REFERENCE>',
                '<INSTANCEPATH>',
                '<NAMESPACEPATH>',
                '<HOST>woot</HOST>',
                '<LOCALNAMESPACEPATH>',
                '<NAMESPACE NAME="root"/>',
                '<NAMESPACE NAME="cimv2"/>',
                '</LOCALNAMESPACEPATH>',
                '</NAMESPACEPATH>',
                '<INSTANCENAME CLASSNAME="CIM_Ref"/>',
                '</INSTANCEPATH>',
                '</VALUE.REFERENCE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, MissingKeybindingsWarning, True  # for the inner ref
    ),
    (
        "Classname with one keybinding, with implied default args",
        dict(
            obj=CIMInstanceName('CIM_Foo', {'Cheepy': 'Birds'}),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="string" TYPE="string">Birds</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding, with specified default args",
        dict(
            obj=CIMInstanceName('CIM_Foo', {'Cheepy': 'Birds'}),
            kwargs=dict(
                ignore_host=False,
                ignore_namespace=False,
            ),
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="string" TYPE="string">Birds</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding, with non-default: ignore_host=True",
        dict(
            obj=CIMInstanceName('CIM_Foo', {'Cheepy': 'Birds'}),
            kwargs=dict(
                ignore_host=True,
            ),
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="string" TYPE="string">Birds</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with one keybinding, with non-def.: ignore_namespace=True",
        dict(
            obj=CIMInstanceName('CIM_Foo', {'Cheepy': 'Birds'}),
            kwargs=dict(
                ignore_namespace=True,
            ),
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Cheepy">',
                '<KEYVALUE VALUETYPE="string" TYPE="string">Birds</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),

    # Classname with mult. keybindings tests
    (
        "Classname with mult. keybindings, with implied default args",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                NocaseDict([
                    ('Name', 'Foo'),
                    ('Number', 42),
                    ('Boolean', False),
                    ('Ref', CIMInstanceName('CIM_Bar')),
                ]),
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Name">',
                '<KEYVALUE VALUETYPE="string" TYPE="string">Foo</KEYVALUE>',
                '</KEYBINDING>',
                '<KEYBINDING NAME="Number">',
                '<KEYVALUE VALUETYPE="numeric">42</KEYVALUE>',
                '</KEYBINDING>',
                '<KEYBINDING NAME="Boolean">',
                '<KEYVALUE VALUETYPE="boolean" TYPE="boolean">FALSE</KEYVALUE>',
                '</KEYBINDING>',
                '<KEYBINDING NAME="Ref">',
                '<VALUE.REFERENCE>',
                '<INSTANCENAME CLASSNAME="CIM_Bar"/>',
                '</VALUE.REFERENCE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, MissingKeybindingsWarning, True  # for the inner ref
    ),
    (
        "Classname with mult. keybindings, with specified default args",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                NocaseDict([
                    ('Name', 'Foo'),
                    ('Number', 42),
                    ('Boolean', False),
                    ('Ref', CIMInstanceName('CIM_Bar')),
                ]),
            ),
            kwargs=dict(
                ignore_host=False,
                ignore_namespace=False,
            ),
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Name">',
                '<KEYVALUE VALUETYPE="string" TYPE="string">Foo</KEYVALUE>',
                '</KEYBINDING>',
                '<KEYBINDING NAME="Number">',
                '<KEYVALUE VALUETYPE="numeric">42</KEYVALUE>',
                '</KEYBINDING>',
                '<KEYBINDING NAME="Boolean">',
                '<KEYVALUE VALUETYPE="boolean" TYPE="boolean">FALSE</KEYVALUE>',
                '</KEYBINDING>',
                '<KEYBINDING NAME="Ref">',
                '<VALUE.REFERENCE>',
                '<INSTANCENAME CLASSNAME="CIM_Bar"/>',
                '</VALUE.REFERENCE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, MissingKeybindingsWarning, True  # for the inner ref
    ),
    (
        "Classname with mult. keybindings, with non-default: ignore_host=True",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                NocaseDict([
                    ('Name', 'Foo'),
                    ('Number', 42),
                    ('Boolean', False),
                    ('Ref', CIMInstanceName('CIM_Bar')),
                ]),
            ),
            kwargs=dict(
                ignore_host=True,
            ),
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Name">',
                '<KEYVALUE VALUETYPE="string" TYPE="string">Foo</KEYVALUE>',
                '</KEYBINDING>',
                '<KEYBINDING NAME="Number">',
                '<KEYVALUE VALUETYPE="numeric">42</KEYVALUE>',
                '</KEYBINDING>',
                '<KEYBINDING NAME="Boolean">',
                '<KEYVALUE VALUETYPE="boolean" TYPE="boolean">FALSE</KEYVALUE>',
                '</KEYBINDING>',
                '<KEYBINDING NAME="Ref">',
                '<VALUE.REFERENCE>',
                '<INSTANCENAME CLASSNAME="CIM_Bar"/>',
                '</VALUE.REFERENCE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, MissingKeybindingsWarning, True  # for the inner ref
    ),
    (
        "Classname with mult. keybindings, with non-def: ignore_namespace=True",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                NocaseDict([
                    ('Name', 'Foo'),
                    ('Number', 42),
                    ('Boolean', False),
                    ('Ref', CIMInstanceName('CIM_Bar')),
                ]),
            ),
            kwargs=dict(
                ignore_namespace=True,
            ),
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="Name">',
                '<KEYVALUE VALUETYPE="string" TYPE="string">Foo</KEYVALUE>',
                '</KEYBINDING>',
                '<KEYBINDING NAME="Number">',
                '<KEYVALUE VALUETYPE="numeric">42</KEYVALUE>',
                '</KEYBINDING>',
                '<KEYBINDING NAME="Boolean">',
                '<KEYVALUE VALUETYPE="boolean" TYPE="boolean">FALSE</KEYVALUE>',
                '</KEYBINDING>',
                '<KEYBINDING NAME="Ref">',
                '<VALUE.REFERENCE>',
                '<INSTANCENAME CLASSNAME="CIM_Bar"/>',
                '</VALUE.REFERENCE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, MissingKeybindingsWarning, True  # for the inner ref
    ),

    # Classname with namespace tests
    (
        "Classname with namespace, with implied default args",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                namespace='root/cimv2',
            ),
            kwargs={},
            exp_xml_str=(
                '<LOCALINSTANCEPATH>',
                '<LOCALNAMESPACEPATH>',
                '<NAMESPACE NAME="root"/>',
                '<NAMESPACE NAME="cimv2"/>',
                '</LOCALNAMESPACEPATH>',
                '<INSTANCENAME CLASSNAME="CIM_Foo"/>',
                '</LOCALINSTANCEPATH>',
            )
        ),
        None, MissingKeybindingsWarning, True
    ),
    (
        "Classname with namespace, with specified default args",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                namespace='root/cimv2',
            ),
            kwargs=dict(
                ignore_host=False,
                ignore_namespace=False,
            ),
            exp_xml_str=(
                '<LOCALINSTANCEPATH>',
                '<LOCALNAMESPACEPATH>',
                '<NAMESPACE NAME="root"/>',
                '<NAMESPACE NAME="cimv2"/>',
                '</LOCALNAMESPACEPATH>',
                '<INSTANCENAME CLASSNAME="CIM_Foo"/>',
                '</LOCALINSTANCEPATH>',
            )
        ),
        None, MissingKeybindingsWarning, True
    ),
    (
        "Classname with namespace, with non-default: ignore_host=True",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                namespace='root/cimv2',
            ),
            kwargs=dict(
                ignore_host=True,
            ),
            exp_xml_str=(
                '<LOCALINSTANCEPATH>',
                '<LOCALNAMESPACEPATH>',
                '<NAMESPACE NAME="root"/>',
                '<NAMESPACE NAME="cimv2"/>',
                '</LOCALNAMESPACEPATH>',
                '<INSTANCENAME CLASSNAME="CIM_Foo"/>',
                '</LOCALINSTANCEPATH>',
            )
        ),
        None, MissingKeybindingsWarning, True
    ),
    (
        "Classname with namespace, with non-def: ignore_namespace=True",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                namespace='root/cimv2',
            ),
            kwargs=dict(
                ignore_namespace=True,
            ),
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo"/>',
            )
        ),
        None, MissingKeybindingsWarning, True
    ),

    # Classname with namespace+host tests
    (
        "Classname with namespace+host, with implied default args",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                namespace='root/cimv2',
                host='woot.com',
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCEPATH>',
                '<NAMESPACEPATH>',
                '<HOST>woot.com</HOST>',
                '<LOCALNAMESPACEPATH>',
                '<NAMESPACE NAME="root"/>',
                '<NAMESPACE NAME="cimv2"/>',
                '</LOCALNAMESPACEPATH>',
                '</NAMESPACEPATH>',
                '<INSTANCENAME CLASSNAME="CIM_Foo"/>',
                '</INSTANCEPATH>',
            )
        ),
        None, MissingKeybindingsWarning, True
    ),
    (
        "Classname with namespace+host, with specified default args",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                namespace='root/cimv2',
                host='woot.com',
            ),
            kwargs=dict(
                ignore_host=False,
                ignore_namespace=False,
            ),
            exp_xml_str=(
                '<INSTANCEPATH>',
                '<NAMESPACEPATH>',
                '<HOST>woot.com</HOST>',
                '<LOCALNAMESPACEPATH>',
                '<NAMESPACE NAME="root"/>',
                '<NAMESPACE NAME="cimv2"/>',
                '</LOCALNAMESPACEPATH>',
                '</NAMESPACEPATH>',
                '<INSTANCENAME CLASSNAME="CIM_Foo"/>',
                '</INSTANCEPATH>',
            )
        ),
        None, MissingKeybindingsWarning, True
    ),
    (
        "Classname with namespace+host, with non-default: ignore_host=True",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                namespace='root/cimv2',
                host='woot.com',
            ),
            kwargs=dict(
                ignore_host=True,
            ),
            exp_xml_str=(
                '<LOCALINSTANCEPATH>',
                '<LOCALNAMESPACEPATH>',
                '<NAMESPACE NAME="root"/>',
                '<NAMESPACE NAME="cimv2"/>',
                '</LOCALNAMESPACEPATH>',
                '<INSTANCENAME CLASSNAME="CIM_Foo"/>',
                '</LOCALINSTANCEPATH>',
            )
        ),
        None, MissingKeybindingsWarning, True
    ),
    (
        "Classname with namespace+host, with non-def: ignore_namespace=True",
        dict(
            obj=CIMInstanceName(
                'CIM_Foo',
                namespace='root/cimv2',
                host='woot.com',
            ),
            kwargs=dict(
                ignore_namespace=True,
            ),
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo"/>',
            )
        ),
        None, MissingKeybindingsWarning, True
    ),

    # Tests with invalid keybinding values
    (
        "Keybinding value with invalid datetime type",
        dict(
            obj=CIMINSTANCENAME_INV_KEYBINDINGS_1,
            kwargs={},
            exp_xml_str=None
        ),
        TypeError, None, True
    ),
    (
        "Keybinding value with invalid CIMParameter type",
        dict(
            obj=CIMINSTANCENAME_INV_KEYBINDINGS_2,
            kwargs={},
            exp_xml_str=None
        ),
        TypeError, None, True
    ),
    (
        "Keybinding value with invalid CIMProperty type",
        dict(
            obj=CIMINSTANCENAME_INV_KEYBINDINGS_3,
            kwargs={},
            exp_xml_str=None
        ),
        TypeError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCENAME_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMInstanceName_tocimxml(testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMInstanceName.tocimxml().
    """

    # The code to be tested
    obj_xml = obj.tocimxml(**kwargs)

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

    obj_xml_str = obj_xml.toxml()
    exp_xml_str = ''.join(exp_xml_str)
    validate_cim_xml_obj(obj, obj_xml_str, exp_xml_str)


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCENAME_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMInstanceName_tocimxmlstr(testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMInstanceName.tocimxmlstr().
    """

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(**kwargs)

    # 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(obj_xml_str, str)

    exp_xml_str = ''.join(exp_xml_str)
    validate_cim_xml_obj(obj, obj_xml_str, exp_xml_str)


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCENAME_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMInstanceName_tocimxmlstr_indent_int(
        testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMInstanceName.tocimxmlstr() with indent as integer.
    """

    indent = 4
    indent_str = ' ' * indent

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(indent=indent, **kwargs)

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

    obj_xml = obj.tocimxml(**kwargs)  # This is tested elsewhere
    exp_xml_str = obj_xml.toprettyxml(indent=indent_str)

    assert obj_xml_str == exp_xml_str, (
        f"{obj.__class__.__name__}.tocimxmlstr(indent={indent!r}) "
        "returns unexpected CIM-XML string")


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCENAME_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMInstanceName_tocimxmlstr_indent_str(
        testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMInstanceName.tocimxmlstr() with indent as string.
    """

    indent = 4
    indent_str = ' ' * indent

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(indent=indent_str, **kwargs)

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

    obj_xml = obj.tocimxml(**kwargs)  # This is tested elsewhere
    exp_xml_str = obj_xml.toprettyxml(indent=indent_str)

    assert obj_xml_str == exp_xml_str, (
        f"{obj.__class__.__name__}.tocimxmlstr(indent={indent_str!r}) "
        "returns unexpected CIM-XML string")


TESTCASES_CIMINSTANCENAME_TOCIMXML_SPECIAL_KB = [

    # Testcases for CIMInstanceName.tocimxml(), where the value of an
    # existing keybinding is set to a special value before the test.

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: CIMInstanceName object to be tested.
    #   * kb_name: Name of the keybinding to be set.
    #   * kb_value: New value for the keybinding to be set.
    #   * exp_xml_str: Expected CIM-XML string, as a tuple/list of parts.
    # * 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

    (
        "Keybinding with invalid type CIMParameter",
        dict(
            obj=CIMInstanceName('CIM_Foo', keybindings=[('K1', 'V1')]),
            kb_name='K1',
            kb_value=CIMParameter('C2', type='string'),  # invalid type
            exp_xml_str=None,
        ),
        TypeError, None, True
    ),
    (
        "Keybinding with binary string",
        # Note: This testcase cannot be specified in the standard testcases
        # for tocimxml(), because CIMInstanceName.__init__() ensures
        # that the keybinding values get converted to unicode. The
        # tocimxml() method still needs to handle this case because the
        # keybindng values can be set via the mutable dictionary.
        dict(
            obj=CIMInstanceName('CIM_Foo', keybindings=[('K1', 'V1')]),
            kb_name='K1',
            kb_value=b'V2',
            exp_xml_str=(
                '<INSTANCENAME CLASSNAME="CIM_Foo">',
                '<KEYBINDING NAME="K1">',
                '<KEYVALUE VALUETYPE="string" TYPE="string">V2</KEYVALUE>',
                '</KEYBINDING>',
                '</INSTANCENAME>',
            )
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCENAME_TOCIMXML_SPECIAL_KB)
@simplified_test_function
@log_entry_exit
def test_CIMInstanceName_tocimxml_special_kb(
        testcase, obj, kb_name, kb_value, exp_xml_str):
    """
    Test function for CIMInstanceName.tocimxml(), where the value of an
    existing keybinding is set to a special value before the test.
    """

    # Set existing keybinding to the new value. Any invalid types etc. are
    # not checked, because this simply modifies the (mutable) keybindings
    # dictionary.
    obj.keybindings[kb_name] = kb_value
    assert obj.keybindings[kb_name] == kb_value

    # The code to be tested
    obj_xml = obj.tocimxml()

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

    obj_xml_str = obj_xml.toxml()
    exp_xml_str = ''.join(exp_xml_str)
    validate_cim_xml_obj(obj, obj_xml_str, exp_xml_str)


TESTCASES_CIMINSTANCENAME_FROM_WBEM_URI = [

    # Testcases for CIMInstanceName.from_wbem_uri()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * uri: WBEM URI string to be tested.
    #   * exp_attrs: Dict of all expected attributes of created object, 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

    (
        "all components, normal case",
        dict(
            uri='https://10.11.12.13:5989/root/cimv2:CIM_Foo.k1="v1"',
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
        ),
        None, None, True
    ),
    (
        "no authority",
        dict(
            uri='https:/root/cimv2:CIM_Foo.k1="v1"',
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host=None),
        ),
        None, None, True
    ),
    (
        "authority with user:password",
        dict(
            uri='https://jdd:test@10.11.12.13:5989/root/cimv2:CIM_Foo.k1="v1"',
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host='jdd:test@10.11.12.13:5989'),
        ),
        None, None, True
    ),
    (
        "authority with user (no password)",
        dict(
            uri='https://jdd@10.11.12.13:5989/root/cimv2:CIM_Foo.k1="v1"',
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host='jdd@10.11.12.13:5989'),
        ),
        None, None, True
    ),
    (
        "authority without port",
        dict(
            uri='https://10.11.12.13/root/cimv2:CIM_Foo.k1="v1"',
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host='10.11.12.13'),
        ),
        None, None, True
    ),
    (
        "authority with IPv6 address",
        dict(
            uri='https://[10:11:12:13]/root/cimv2:CIM_Foo.k1="v1"',
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host='[10:11:12:13]'),
        ),
        None, None, True
    ),
    (
        "authority with IPv6 address and port",
        dict(
            uri='https://[10:11:12:13]:5989/root/cimv2:CIM_Foo.k1="v1"',
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host='[10:11:12:13]:5989'),
        ),
        None, None, True
    ),
    (
        "no namespace type",
        dict(
            uri='//10.11.12.13:5989/root/cimv2:CIM_Foo.k1="v1"',
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
        ),
        None, None, True
    ),
    (
        "namespace type http",
        dict(
            uri='http://10.11.12.13:5989/root/cimv2:CIM_Foo.k1="v1"',
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
        ),
        None, None, True
    ),
    (
        "namespace type upper case HTTP",
        dict(
            uri='HTTP://10.11.12.13:5989/root/cimv2:CIM_Foo.k1="v1"',
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
        ),
        None, None, True
    ),
    (
        "namespace type mixed case HttpS",
        dict(
            uri='HttpS://10.11.12.13:5989/root/cimv2:CIM_Foo.k1="v1"',
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
        ),
        None, None, True
    ),
    (
        "namespace type cimxml-wbem",
        dict(
            uri='cimxml-wbem://10.11.12.13:5989/root/cimv2:CIM_Foo.k1="v1"',
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
        ),
        None, None, True
    ),
    (
        "namespace type cimxml-wbems",
        dict(
            uri='cimxml-wbems://10.11.12.13:5989/root/cimv2:CIM_Foo.k1="v1"',
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
        ),
        None, None, True
    ),
    (
        "unknown namespace type",
        dict(
            uri='xyz://10.11.12.13:5989/root/cimv2:CIM_Foo.k1="v1"',
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
        ),
        None, UserWarning, True
    ),
    (
        "local WBEM URI (with initial slash)",
        dict(
            uri='/root/cimv2:CIM_Foo.k1="v1"',
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host=None),
        ),
        None, None, True
    ),
    (
        "local WBEM URI (with missing initial slash)",
        dict(
            uri='root/cimv2:CIM_Foo.k1="v1"',
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host=None),
        ),
        None, None, True
    ),
    (
        "local WBEM URI with only class name (with initial slash+colon)",
        dict(
            uri='/:CIM_Foo.k1="v1"',
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace=None,
                host=None),
        ),
        None, None, True
    ),
    (
        "local WBEM URI with only class name (without initial slash)",
        dict(
            uri=':CIM_Foo.k1="v1"',
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace=None,
                host=None),
        ),
        None, None, True
    ),
    (
        "local WBEM URI with only class name (without initial slash+colon)",
        dict(
            uri='CIM_Foo.k1="v1"',
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace=None,
                host=None),
        ),
        None, None, True
    ),
    (
        "local WBEM URI with namespace that has only one component",
        dict(
            uri='/root:CIM_Foo.k1="v1"',
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root',
                host=None),
        ),
        None, None, True
    ),
    (
        "local WBEM URI with namespace that has three components",
        dict(
            uri='/root/cimv2/test:CIM_Foo.k1="v1"',
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2/test',
                host=None),
        ),
        None, None, True
    ),
    (
        "multiple keys (bool) - in alphabetical order",
        dict(
            uri='/n:C.k1=false,k2=true,k3=False,k4=True,k5=FALSE,k6=TRUE',
            exp_attrs=dict(
                classname='C',
                keybindings=NocaseDict([
                    ('k1', False), ('k2', True),
                    ('k3', False), ('k4', True),
                    ('k5', False), ('k6', True)]),
                namespace='n',
                host=None),
        ),
        None, None, True
    ),
    (
        "multiple keys (bool) - in non-alphabetical order",
        dict(
            uri='/n:C.k1=false,k3=False,k2=true,k4=True,k5=FALSE,k6=TRUE',
            exp_attrs=dict(
                classname='C',
                keybindings=NocaseDict([
                    ('k1', False), ('k3', False),
                    ('k2', True), ('k4', True),
                    ('k5', False), ('k6', True)]),
                namespace='n',
                host=None),
        ),
        None, None, True
    ),
    (
        "multiple keys (int) - in non-alphabetical order",
        dict(
            uri='/n:C.k1=0,k2=-1,k3=-32769,k4=42,k5=+42,'
                'kmax32=4294967295,'
                'kmin32=-4294967296,'
                'kmax64=9223372036854775807,'
                'kmin64=-9223372036854775808,'
                'klong=9223372036854775808',
            exp_attrs=dict(
                classname='C',
                keybindings=NocaseDict([
                    ('k1', 0), ('k2', -1), ('k3', -32769),
                    ('k4', 42), ('k5', 42),
                    ('kmax32', 4294967295),
                    ('kmin32', -4294967296),
                    ('kmax64', 9223372036854775807),
                    ('kmin64', -9223372036854775808),
                    ('klong', 9223372036854775808)]),
                namespace='n',
                host=None),
        ),
        None, None, True
    ),
    (
        "int key with invalid decimal digit U+0661 (ARABIC-INDIC ONE)",
        dict(
            uri='/n:C.k1=\u0661',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "int key with invalid decimal digit U+1D7CF (MATHEM. BOLD ONE)",
        dict(
            uri='/n:C.k1=\U0001d7cf',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "int key with invalid decimal digit U+2081 (SUBSCRIPT ONE)",
        dict(
            uri='/n:C.k1=\u2081',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "int key with invalid decimal digit U+00B9 (SUPERSCRIPT ONE)",
        dict(
            uri='/n:C.k1=\u00b9',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "int key with invalid non-decimal digit U+10A44 (KHAROSHTHI TEN)",
        dict(
            uri='/n:C.k1=\U00010a44',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "positive int key in octal representation",
        dict(
            uri='/n:C.k1=015',
            exp_attrs=dict(
                classname='C',
                keybindings=NocaseDict([('k1', 13)]),
                namespace='n',
                host=None),
        ),
        None, None, True
    ),
    (
        "negative int key in octal representation",
        dict(
            uri='/n:C.k1=-017',
            exp_attrs=dict(
                classname='C',
                keybindings=NocaseDict([('k1', -15)]),
                namespace='n',
                host=None),
        ),
        None, None, True
    ),
    (
        "int key in octal representation with invalid octal digits",
        dict(
            uri='/n:C.k1=018',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "positive int key in binary representation (upper case)",
        dict(
            uri='/n:C.k1=0101B',
            exp_attrs=dict(
                classname='C',
                keybindings=NocaseDict([('k1', 5)]),
                namespace='n',
                host=None),
        ),
        None, None, True
    ),
    (
        "negative int key in binary representation (lower case)",
        dict(
            uri='/n:C.k1=-0101b',
            exp_attrs=dict(
                classname='C',
                keybindings=NocaseDict([('k1', -5)]),
                namespace='n',
                host=None),
        ),
        None, None, True
    ),
    (
        "int key in binary representation with invalid binary digits",
        dict(
            uri='/n:C.k1=0102',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "positive int key in hex representation (upper case)",
        dict(
            uri='/n:C.k1=0X19AF',
            exp_attrs=dict(
                classname='C',
                keybindings=NocaseDict([('k1', 0x19AF)]),
                namespace='n',
                host=None),
        ),
        None, None, True
    ),
    (
        "negative int key in hex representation (lower case)",
        dict(
            uri='/n:C.k1=-0x19af',
            exp_attrs=dict(
                classname='C',
                keybindings=NocaseDict([('k1', -0x19AF)]),
                namespace='n',
                host=None),
        ),
        None, None, True
    ),
    (
        "int key in hex representation with invalid hex digits",
        dict(
            uri='/n:C.k1=0x19afg',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "multiple float keys - in non-alphabetical order",
        dict(
            uri='/n:C.k1=.0,k2=-0.1,k3=+.1,k4=+31.4E-1,k5=.4e1,'
                'kmax32=3.402823466E38,'
                'kmin32=1.175494351E-38,'
                'kmax64=1.7976931348623157E308,'
                'kmin64=4.9E-324',
            exp_attrs=dict(
                classname='C',
                keybindings=NocaseDict([
                    ('k1', 0.0), ('k2', -0.1), ('k3', 0.1),
                    ('k4', 31.4E-1), ('k5', 0.4E1),
                    ('kmax32', 3.402823466E38),
                    ('kmin32', 1.175494351E-38),
                    ('kmax64', 1.7976931348623157E308),
                    ('kmin64', 4.9E-324),
                ]),
                namespace='n',
                host=None),
        ),
        None, None, True
    ),
    (
        "invalid float key 1. (not allowed in realValue)",
        dict(
            uri='/n:C.k1=1.',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "float key with special value INF (allowed by extension)",
        dict(
            uri='/n:C.k1=INF',
            exp_attrs=dict(
                classname='C',
                keybindings=NocaseDict(k1=float('inf')),
                namespace='n',
                host=None),
        ),
        None, None, True
    ),
    (
        "float key with special value -INF (allowed by extension)",
        dict(
            uri='/n:C.k1=-INF',
            exp_attrs=dict(
                classname='C',
                keybindings=NocaseDict(k1=float('-inf')),
                namespace='n',
                host=None),
        ),
        None, None, True
    ),
    (
        "float key with special value NAN (allowed by extension)",
        dict(
            uri='/n:C.k1=NAN',
            exp_attrs=dict(
                classname='C',
                keybindings=NocaseDict(k1=float('nan')),
                namespace='n',
                host=None),
        ),
        None, None, False  # float('nan') does not compare equal to itself
    ),
    (
        "multiple string keys - in alphabetical order",
        dict(
            uri=r'/n:C.k1="",k2="a",k3="42",k4="\"",k5="\\",k6="\\\"",k7="\'"',
            exp_attrs=dict(
                classname='C',
                keybindings=NocaseDict([
                    ('k1', ''), ('k2', 'a'), ('k3', '42'), ('k4', '"'),
                    ('k5', '\\'), ('k6', '\\"'), ('k7', "'"),
                ]),
                namespace='n',
                host=None),
        ),
        None, None, True
    ),
    (
        "string key with keybindings syntax in its value",
        dict(
            uri=r'/n:C.k1="k2=42,k3=3"',
            exp_attrs=dict(
                classname='C',
                keybindings=NocaseDict(k1='k2=42,k3=3'),
                namespace='n',
                host=None),
        ),
        None, None, True
    ),
    (
        "multiple char16 keys - in non-alphabetical order",
        dict(
            uri="/n:C.k1='a',k3='\"',k2='1',k4='\\'',k5='\\\\'",
            exp_attrs=dict(
                classname='C',
                keybindings=NocaseDict([
                    ('k1', 'a'), ('k3', '"'), ('k2', '1'), ('k4', "'"),
                    ('k5', '\\'),
                ]),
                namespace='n',
                host=None),
        ),
        None, None, True
    ),
    (
        "empty char16 key",
        dict(
            uri="/n:C.k1=''",
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "invalid char16 key with two characters",
        dict(
            uri="/n:C.k1='ab'",
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "datetime key for point in time (in quotes)",
        dict(
            uri='/n:C.k1="19980125133015.123456-300"',
            exp_attrs=dict(
                classname='C',
                keybindings=NocaseDict([
                    ('k1', CIMDateTime('19980125133015.123456-300')),
                ]),
                namespace='n',
                host=None),
        ),
        None, None, True
    ),
    (
        "datetime key for interval (in quotes)",
        dict(
            uri='/n:C.k1="12345678133015.123456:000"',
            exp_attrs=dict(
                classname='C',
                keybindings=NocaseDict([
                    ('k1', CIMDateTime('12345678133015.123456:000')),
                ]),
                namespace='n',
                host=None),
        ),
        None, None, True
    ),
    (
        "datetime key for point in time (no quotes)",
        dict(
            uri='/n:C.k1=19980125133015.123456-300',
            exp_attrs=dict(
                classname='C',
                keybindings=NocaseDict([
                    ('k1', CIMDateTime('19980125133015.123456-300')),
                ]),
                namespace='n',
                host=None),
        ),
        None, UserWarning, True
    ),
    (
        "datetime key for interval (no quotes)",
        dict(
            uri='/n:C.k1=12345678133015.123456:000',
            exp_attrs=dict(
                classname='C',
                keybindings=NocaseDict([
                    ('k1', CIMDateTime('12345678133015.123456:000')),
                ]),
                namespace='n',
                host=None),
        ),
        None, UserWarning, True
    ),
    (
        "reference key that references an instance that has an int key "
        "(normal association)",
        dict(
            uri='/n1:C1.k1="/n2:C2.k2=1"',
            exp_attrs=dict(
                classname='C1',
                keybindings=NocaseDict([
                    ('k1', CIMInstanceName(
                        classname='C2',
                        keybindings=NocaseDict([
                            ('k2', 1),
                        ]),
                        namespace='n2')),
                ]),
                namespace='n1',
                host=None),
        ),
        None, None, True
    ),
    (
        "reference key that references an instance that has a string key "
        "(normal association)",
        dict(
            uri=r'/n1:C1.k1="/n2:C2.k2=\"v2\""',
            exp_attrs=dict(
                classname='C1',
                keybindings=NocaseDict([
                    ('k1', CIMInstanceName(
                        classname='C2',
                        keybindings=NocaseDict([
                            ('k2', 'v2'),
                        ]),
                        namespace='n2')),
                ]),
                namespace='n1',
                host=None),
        ),
        None, None, True
    ),
    (
        "reference key that references an instance that has no keys "
        "(with dot)",
        # Pywbem interprets the key value as a string typed value, because it
        # does not match the format of a WBEM URI which requires at least one
        # key-value pair.
        dict(
            uri=r'/n1:C1.k1="/n2:C2."',
            exp_attrs=dict(
                classname='C1',
                keybindings=NocaseDict([
                    ('k1', '/n2:C2.'),
                ]),
                namespace='n1',
                host=None),
        ),
        None, None, True
    ),
    (
        "reference key that references an instance that has no keys "
        "(without dot)",
        # Pywbem interprets the key value as a string typed value, because it
        # does not match the format of a WBEM URI which requires at least one
        # key-value pair.
        dict(
            uri=r'/n1:C1.k1="/n2:C2"',
            exp_attrs=dict(
                classname='C1',
                keybindings=NocaseDict([
                    ('k1', '/n2:C2'),
                ]),
                namespace='n1',
                host=None),
        ),
        None, None, True
    ),
    (
        "double nested reference to int key (association to association)",
        dict(
            uri=r'/n1:C1.k1="/n2:C2.k2=\"/n3:C3.k3=3\""',
            exp_attrs=dict(
                classname='C1',
                keybindings=NocaseDict([
                    ('k1', CIMInstanceName(
                        classname='C2',
                        keybindings=NocaseDict([
                            ('k2', CIMInstanceName(
                                classname='C3',
                                keybindings=NocaseDict([
                                    ('k3', 3),
                                ]),
                                namespace='n3')),
                        ]),
                        namespace='n2')),
                ]),
                namespace='n1',
                host=None,
            ),
        ),
        None, None, True
    ),
    (
        "double nested reference to string key (association to "
        "association)",
        dict(
            uri=r'/n1:C1.k1="/n2:C2.k2=\"/n3:C3.k3=\\\"v3\\\"\""',
            exp_attrs=dict(
                classname='C1',
                keybindings=NocaseDict([
                    ('k1', CIMInstanceName(
                        classname='C2',
                        keybindings=NocaseDict([
                            ('k2', CIMInstanceName(
                                classname='C3',
                                keybindings=NocaseDict([
                                    ('k3', 'v3'),
                                ]),
                                namespace='n3')),
                        ]),
                        namespace='n2')),
                ]),
                namespace='n1',
                host=None,
            ),
        ),
        None, None, True
    ),
    (
        "missing delimiter / before authority",
        dict(
            uri='https:/10.11.12.13/cimv2:CIM_Foo.k1="v1"',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "invalid char ; in authority",
        dict(
            uri='https://10.11.12.13;5989/cimv2:CIM_Foo.k1="v1"',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "delimiter / before namespace replaced with :",
        dict(
            uri='https://10.11.12.13:5989:root:CIM_Foo.k1="v1"',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "delimiter : before classname replaced with .",
        dict(
            uri='https://10.11.12.13:5989/root.CIM_Foo.k1="v1"',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "invalid '/' between namespace and classname",
        dict(
            uri='https://10.11.12.13:5989/cimv2/CIM_Foo.k1="v1"',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "class name missing",
        dict(
            uri='https://10.11.12.13:5989/root/cimv2:k1="v1"',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "class path used as instance path",
        dict(
            uri='https://10.11.12.13:5989/root:CIM_Foo',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "invalid ':' between classname and key k1",
        dict(
            uri='https://10.11.12.13:5989/cimv2:CIM_Foo:k1="v1"',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "invalid '/' between classname and key k1",
        dict(
            uri='https://10.11.12.13:5989/cimv2:CIM_Foo/k1="v1"',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "invalid '.' between key k1 and key k2",
        dict(
            uri='https://10.11.12.13:5989/cimv2:CIM_Foo.k1="v1".k2="v2"',
            exp_attrs=None,
        ),
        ValueError, None, False
    ),
    (
        "invalid ':' between key k1 and key k2",
        dict(
            uri='https://10.11.12.13:5989/cimv2:CIM_Foo.k1="v1":k2="v2"',
            exp_attrs=None,
        ),
        ValueError, None, False
    ),
    (
        "double quotes missing around string value of key k2",
        dict(
            uri='https://10.11.12.13:5989/cimv2:CIM_Foo.k1="v1".k2=v2',
            exp_attrs=None,
        ),
        ValueError, None, False
    ),
    (
        "invalid double comma between keybindings",
        dict(
            uri='https://10.11.12.13:5989/cimv2:CIM_Foo.k1="v1",,k2=42',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "invalid comma after last keybinding",
        dict(
            uri='https://10.11.12.13:5989/cimv2:CIM_Foo.k1="v1",k2=42,',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "equal sign missing in keyinding",
        dict(
            uri='https://10.11.12.13:5989/cimv2:CIM_Foo.k1="v1",k242',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "double equal sign in keyinding",
        dict(
            uri='https://10.11.12.13:5989/cimv2:CIM_Foo.k1="v1",k2==42',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCENAME_FROM_WBEM_URI)
@simplified_test_function
@log_entry_exit
def test_CIMInstanceName_from_wbem_uri(testcase, uri, exp_attrs):
    """
    Test function for CIMInstanceName.from_wbem_uri()
    """

    # The code to be tested
    obj = CIMInstanceName.from_wbem_uri(uri)

    # 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(obj, CIMInstanceName)

    exp_classname = exp_attrs['classname']
    assert obj.classname == exp_classname
    assert isinstance(obj.classname, type(exp_classname))

    exp_keybindings = exp_attrs['keybindings']
    assert obj.keybindings == exp_keybindings
    assert isinstance(obj.keybindings, type(exp_keybindings))

    exp_namespace = exp_attrs['namespace']
    assert obj.namespace == exp_namespace
    assert isinstance(obj.namespace, type(exp_namespace))

    exp_host = exp_attrs['host']
    assert obj.host == exp_host
    assert isinstance(obj.host, type(exp_host))


TESTCASES_CIMINSTANCENAME_FROM_INSTANCE = [

    # Testcases for CIMInstanceName.from_instance()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * cls_kwargs: Dict with attributes from which test class is constructed
    #   * inst_kwargs: Dict with attributes from which test inst is constructed
    #   * exp_result: Dict of all expected attrs of resulting CIMInstanceName
    #   * namespace: Value of namespace attribute on from_instance() call
    #   * host: Value of host attribute on from_instance() call
    #   * strict: Value of strict attribute on from_instance() call
    # * 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

    (
        "Class with single key",
        dict(
            cls_kwargs=dict(
                classname='CIM_Foo',
                properties=[
                    CIMProperty(
                        'P1', None, type='string',
                        qualifiers=[
                            CIMQualifier('Key', value=True)
                        ]
                    ),
                    CIMProperty('P2', value='Cheese'),
                ]
            ),
            inst_kwargs=dict(
                classname='CIM_Foo',
                properties=[
                    CIMProperty('P1', value='Ham'),
                    CIMProperty('P2', value='Cheese'),
                ]
            ),
            namespace=None,
            host=None,
            strict=True,
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(P1='Ham')
            ),
        ),
        None, None, True
    ),
    (
        "Class with two keys",
        dict(
            cls_kwargs=dict(
                classname='CIM_Foo',
                properties=[
                    CIMProperty(
                        'P1', None, type='string',
                        qualifiers=[
                            CIMQualifier('Key', value=True)
                        ]
                    ),
                    CIMProperty(
                        'P2', None, type='string',
                        qualifiers=[
                            CIMQualifier('Key', value=True)
                        ]
                    ),
                ]
            ),
            inst_kwargs=dict(
                classname='CIM_Foo',
                properties=[
                    CIMProperty('P1', value='Ham'),
                    CIMProperty('P2', value='Cheese'),
                ]
            ),
            namespace=None,
            host=None,
            strict=True,
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict([('P1', 'Ham'), ('P2', 'Cheese')])
            ),
        ),
        None, None, True
    ),
    (
        "Class with key, no key in instance, strict=True (fails)",
        dict(
            cls_kwargs=dict(
                classname='CIM_Foo',
                properties=[
                    CIMProperty(
                        'P1', None, type='string',
                        qualifiers=[
                            CIMQualifier('Key', value=True)
                        ]
                    ),
                    CIMProperty('P2', value='Cheese'),
                ]
            ),
            inst_kwargs=dict(
                classname='CIM_Foo',
                properties=[
                    CIMProperty('P2', value='Cheese'),
                ]
            ),
            namespace=None,
            host=None,
            strict=True,
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "Class with key, no key in instance, strict=False",
        dict(
            cls_kwargs=dict(
                classname='CIM_Foo',
                properties=[
                    CIMProperty(
                        'P1', None, type='string',
                        qualifiers=[
                            CIMQualifier('Key', value=True)
                        ]
                    ),
                    CIMProperty('P2', value='Cheese'),
                ]
            ),
            inst_kwargs=dict(
                classname='CIM_Foo',
                properties=[
                    CIMProperty('P2', value='Cheese'),
                ]
            ),
            namespace=None,
            host=None,
            strict=False,
            exp_attrs=dict(
                classname='CIM_Foo'
            ),
        ),
        None, None, True
    ),
    (
        "Class with two keys, no key in instance, strict=False",
        dict(
            cls_kwargs=dict(
                classname='CIM_Foo',
                properties=[
                    CIMProperty(
                        'P1', None, type='string',
                        qualifiers=[
                            CIMQualifier('Key', value=True)
                        ]
                    ),
                    CIMProperty(
                        'P2', 'DEFAULT', type='string',
                        qualifiers=[
                            CIMQualifier('Key', value=True)
                        ]
                    ),
                ]
            ),
            inst_kwargs=dict(
                classname='CIM_Foo',
                properties=[]
            ),
            namespace=None,
            host=None,
            strict=False,
            exp_attrs=dict(
                classname='CIM_Foo'
            ),
        ),
        None, None, True
    ),
    (
        "Class with reference properies as keys",
        dict(
            cls_kwargs=dict(
                classname='CIM_Ref',
                properties=[
                    CIMProperty(
                        'R1', None, type='reference',
                        qualifiers=[
                            CIMQualifier('Key', value=True)
                        ]
                    ),
                    CIMProperty('R2', type='string', value='Cheese'),
                ]
            ),
            inst_kwargs=dict(
                classname='CIM_Ref',
                properties=[
                    CIMProperty(
                        'R1', value=CIMInstanceName('CIM_X', {'x': "X"})
                    ),
                ]
            ),
            namespace=None,
            host=None,
            strict=True,
            exp_attrs=dict(
                classname='CIM_Ref',
                keybindings=NocaseDict(R1=CIMInstanceName('CIM_X', {'x': "X"}))
            ),
        ),
        None, None, True
    ),

    # namespace / host tests
    (
        "Instance name with namespace and host",
        dict(
            cls_kwargs=dict(
                classname='CIM_Foo',
                properties=[
                    CIMProperty(
                        'P1', None, type='string',
                        qualifiers=[
                            CIMQualifier('Key', value=True)
                        ]
                    ),
                    CIMProperty('P2', value='Cheese'),
                ]
            ),
            inst_kwargs=dict(
                classname='CIM_Foo',
                properties=[
                    CIMProperty('P1', value='Ham'),
                    CIMProperty('P2', value='Cheese'),
                ]
            ),
            namespace='root/blah',
            host='Fred',
            strict=True,
            exp_attrs=dict(
                classname='CIM_Foo',
                keybindings=NocaseDict(P1='Ham'),
                namespace='root/blah',
                host='Fred'
            ),
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCENAME_FROM_INSTANCE)
@simplified_test_function
@log_entry_exit
def test_CIMInstanceName_from_instance(
        testcase, cls_kwargs, inst_kwargs, exp_attrs, namespace, host, strict):
    # pylint: disable=unused-argument
    """
    Test function for CIMInstanceName.from_instance()
    """

    cls = CIMClass(**cls_kwargs)
    inst = CIMInstance(**inst_kwargs)

    # The code to be tested
    obj = CIMInstanceName.from_instance(
        cls, inst, namespace=namespace, host=host, strict=strict)

    assert isinstance(obj, CIMInstanceName)

    exp_classname = exp_attrs['classname']
    assert obj.classname == exp_classname
    assert isinstance(obj.classname, type(exp_classname))

    exp_keybindings = exp_attrs.get('keybindings', NocaseDict())
    assert obj.keybindings == exp_keybindings
    assert isinstance(obj.keybindings, type(exp_keybindings))

    exp_namespace = exp_attrs.get('namespace', None)
    assert obj.namespace == exp_namespace
    assert isinstance(obj.namespace, type(exp_namespace))

    exp_host = exp_attrs.get('host', None)
    assert obj.host == exp_host
    assert isinstance(obj.host, type(exp_host))


TESTCASES_CIMINSTANCENAME_TO_WBEM_URI = [

    # Testcases for CIMInstanceName.to_wbem_uri()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: CIMInstanceName object to be tested.
    #   * kwargs: Keyword arguments for to_wbem_uri():
    #     * format: Format for to_wbem_uri(): one of 'standard', 'canonical',
    #       'cimobject', 'historical'.
    #   * exp_uri: Expected WBEM URI string, or None if failed.
    # * 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

    (
        "all components, standard format",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='//10.11.12.13:5989/root/cimv2:CIM_Foo.k1="v1"',
        ),
        None, None, True
    ),
    (
        "all components, canonical format",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1='V1'),
                namespace='root/CIMv2',
                host='MyHost:5989'),
            kwargs=dict(
                format='canonical',
            ),
            exp_uri='//myhost:5989/root/cimv2:cim_foo.k1="V1"',
        ),
        None, None, True
    ),
    (
        "all components, cimobject format (host is ignored)",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
            kwargs=dict(
                format='cimobject',
            ),
            exp_uri='/root/cimv2:CIM_Foo.k1="v1"',
        ),
        None, None, True
    ),
    (
        "all components, historical format",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
            kwargs=dict(
                format='historical',
            ),
            exp_uri='//10.11.12.13:5989/root/cimv2:CIM_Foo.k1="v1"',
        ),
        None, None, True
    ),
    (
        "all components, invalid format",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1='V1'),
                namespace='root/CIMv2',
                host='MyHost:5989'),
            kwargs=dict(
                format='xxx_invalid_format',
            ),
            exp_uri=None,
        ),
        ValueError, None, True
    ),
    (
        "no authority, standard format",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/root/cimv2:CIM_Foo.k1="v1"',
        ),
        None, None, True
    ),
    (
        "no authority, canonical format",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1='V1'),
                namespace='root/CIMv2',
                host=None),
            kwargs=dict(
                format='canonical',
            ),
            exp_uri='/root/cimv2:cim_foo.k1="V1"',
        ),
        None, None, True
    ),
    (
        "no authority, cimobject format",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host=None),
            kwargs=dict(
                format='cimobject',
            ),
            exp_uri='root/cimv2:CIM_Foo.k1="v1"',
        ),
        None, None, True
    ),
    (
        "no authority, historical format",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host=None),
            kwargs=dict(
                format='historical',
            ),
            exp_uri='root/cimv2:CIM_Foo.k1="v1"',
        ),
        None, None, True
    ),
    (
        "authority with user:password",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host='jdd:test@10.11.12.13:5989'),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='//jdd:test@10.11.12.13:5989/root/cimv2:CIM_Foo.k1="v1"',
        ),
        None, None, True
    ),
    (
        "authority with user (no password)",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host='jdd@10.11.12.13:5989'),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='//jdd@10.11.12.13:5989/root/cimv2:CIM_Foo.k1="v1"',
        ),
        None, None, True
    ),
    (
        "authority without port",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host='10.11.12.13'),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='//10.11.12.13/root/cimv2:CIM_Foo.k1="v1"',
        ),
        None, None, True
    ),
    (
        "authority with IPv6 address",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host='[10:11:12:13]'),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='//[10:11:12:13]/root/cimv2:CIM_Foo.k1="v1"',
        ),
        None, None, True
    ),
    (
        "authority with IPv6 address and port",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host='[10:11:12:13]:5989'),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='//[10:11:12:13]:5989/root/cimv2:CIM_Foo.k1="v1"',
        ),
        None, None, True
    ),
    (
        "local WBEM URI",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/root/cimv2:CIM_Foo.k1="v1"',
        ),
        None, None, True
    ),
    (
        "local WBEM URI with only class name, standard format",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace=None,
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/:CIM_Foo.k1="v1"',
        ),
        None, None, True
    ),
    (
        "local WBEM URI with only class name, canonical format",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(K1='V1'),
                namespace=None,
                host=None),
            kwargs=dict(
                format='canonical',
            ),
            exp_uri='/:cim_foo.k1="V1"',
        ),
        None, None, True
    ),
    (
        "local WBEM URI with only class name, cimobject format",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace=None,
                host=None),
            kwargs=dict(
                format='cimobject',
            ),
            exp_uri=':CIM_Foo.k1="v1"',
        ),
        None, None, True
    ),
    (
        "local WBEM URI with only class name, historical format",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace=None,
                host=None),
            kwargs=dict(
                format='historical',
            ),
            exp_uri='CIM_Foo.k1="v1"',
        ),
        None, None, True
    ),
    (
        "local WBEM URI with namespace that has only one component",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/root:CIM_Foo.k1="v1"',
        ),
        None, None, True
    ),
    (
        "local WBEM URI with namespace that has three components",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2/test',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/root/cimv2/test:CIM_Foo.k1="v1"',
        ),
        None, None, True
    ),
    (
        "multiple keys (bool) in alphabetical order, standard format",
        dict(
            obj=CIMInstanceName(
                classname='C',
                keybindings=NocaseDict([('k1', False), ('k2', True)]),
                namespace='n',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/n:C.k1=FALSE,k2=TRUE',
        ),
        None, None, True
    ),
    (
        "multiple keys (bool) in alphabetical order (lower-cased), "
        "canonical format",
        dict(
            obj=CIMInstanceName(
                classname='C',
                keybindings=NocaseDict([('j1', False), ('K2', True)]),
                namespace='N',
                host=None),
            kwargs=dict(
                format='canonical',
            ),
            exp_uri='/n:c.j1=FALSE,k2=TRUE',
        ),
        None, None, True
    ),
    (
        "multiple keys (bool) in non-alphabetical order in lower case, "
        "standard format",
        dict(
            obj=CIMInstanceName(
                classname='C',
                keybindings=NocaseDict([('k2', True), ('k1', False)]),
                namespace='n',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/n:C.k1=FALSE,k2=TRUE',
        ),
        None, None, True
    ),
    (
        "multiple keys (bool) in non-alphabetical order in mixed case, "
        "standard format",
        dict(
            obj=CIMInstanceName(
                classname='C',
                keybindings=NocaseDict([('K2', True), ('k1', False)]),
                namespace='n',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/n:C.K2=TRUE,k1=FALSE',
        ),
        None, None, True
    ),
    (
        "multiple keys (bool) in non-alphabetical order (lower-cased), "
        "canonical format",
        dict(
            obj=CIMInstanceName(
                classname='C',
                keybindings=NocaseDict([('K2', True), ('j1', False)]),
                namespace='N',
                host=None),
            kwargs=dict(
                format='canonical',
            ),
            exp_uri='/n:c.j1=FALSE,k2=TRUE',
        ),
        None, None, True
    ),
    (
        "multiple keys (int) - created in non-alphabetical order",
        dict(
            obj=CIMInstanceName(
                classname='C',
                keybindings=NocaseDict([('k1', 0), ('k3', -32769), ('k2', -1),
                                        ('k4', 42), ('k5', 42),
                                        ('kmin32', -4294967296),
                                        ('kmax32', 4294967295),
                                        ('kmin64', -9223372036854775808),
                                        ('kmax64', 9223372036854775807),
                                        ('klong', 9223372036854775808)]),
                namespace='n',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/n:C.k1=0,k2=-1,k3=-32769,k4=42,k5=42,'
            'klong=9223372036854775808,'
            'kmax32=4294967295,'
            'kmax64=9223372036854775807,'
            'kmin32=-4294967296,'
            'kmin64=-9223372036854775808',
        ),
        None, None, True
    ),
    (
        "multiple float keys - created in non-alphabetical order",
        dict(
            obj=CIMInstanceName(
                classname='C',
                keybindings=NocaseDict([('k1', 0.0), ('k3', 0.1), ('k2', -0.1),
                                        ('k4', 31.4E-1), ('k5', 0.4E1),
                                        ('kmin32', 1.175494351E-38),
                                        ('kmax32', 3.402823466E38),
                                        ('kmin64', 2.2250738585072014E-308),
                                        ('kmax64', 1.7976931348623157E308)]),
                namespace='n',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/n:C.k1=0.0,k2=-0.1,k3=0.1,k4=3.14,k5=4.0,'
            'kmax32=3.402823466e+38,'
            'kmax64=1.7976931348623157e+308,'
            'kmin32=1.175494351e-38,'
            'kmin64=2.2250738585072014e-308',
        ),
        None, None, True
    ),
    (
        "float key with special value INF (allowed by extension)",
        dict(
            obj=CIMInstanceName(
                classname='C',
                keybindings=NocaseDict(k1=float('inf')),
                namespace='n',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/n:C.k1=inf',
        ),
        None, None, True
    ),
    (
        "float key with special value -INF (allowed by extension)",
        dict(
            obj=CIMInstanceName(
                classname='C',
                keybindings=NocaseDict(k1=float('-inf')),
                namespace='n',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/n:C.k1=-inf',
        ),
        None, None, True
    ),
    (
        "float key with special value NAN (allowed by extension)",
        dict(
            obj=CIMInstanceName(
                classname='C',
                keybindings=NocaseDict(k1=float('nan')),
                namespace='n',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/n:C.k1=nan',
        ),
        None, None, False  # float('nan') does not compare equal to itself
    ),
    (
        "multiple string keys - created in alphabetical order",
        dict(
            obj=CIMInstanceName(
                classname='C',
                keybindings=NocaseDict([('k1', ''), ('k2', 'a'), ('k3', '42'),
                                        ('k4', '"'), ('k5', '\\'),
                                        ('k6', '\\"'), ('k7', "'")]),
                namespace='n',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/n:C.k1="",k2="a",k3="42",k4="\\\x22",'
            'k5="\\\\",k6="\\\\\\\x22",k7="\x27"',
        ),
        None, None, True
    ),
    (
        "string key with keybindings syntax in its value",
        dict(
            obj=CIMInstanceName(
                classname='C',
                keybindings=NocaseDict(k1='k2=42,k3=3'),
                namespace='n',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/n:C.k1="k2=42,k3=3"',
        ),
        None, None, True
    ),
    (
        "multiple char16 keys - created in non-alphabetical order",
        dict(
            obj=CIMInstanceName(
                classname='C',
                keybindings=NocaseDict([('k1', 'a'), ('k3', '"'), ('k2', '1'),
                                        ('k4', "'"), ('k5', '\\')]),
                namespace='n',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/n:C.k1="a",k2="1",k3="\\"",k4="\'",k5="\\\\"',
        ),
        None, None, True
    ),
    (
        "datetime key for point in time (in quotes)",
        dict(
            obj=CIMInstanceName(
                classname='C',
                keybindings=[
                    ('k1', CIMDateTime('19980125133015.123456-300')),
                ],
                namespace='n',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/n:C.k1="19980125133015.123456-300"',
        ),
        None, None, True
    ),
    (
        "datetime key for interval (in quotes)",
        dict(
            obj=CIMInstanceName(
                classname='C',
                keybindings=[
                    ('k1', CIMDateTime('12345678133015.123456:000')),
                ],
                namespace='n',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/n:C.k1="12345678133015.123456:000"',
        ),
        None, None, True
    ),
    (
        "reference key that has an int key (normal association), "
        "standard format",
        dict(
            obj=CIMInstanceName(
                classname='C1',
                keybindings=[
                    ('k1', CIMInstanceName(
                        classname='C2',
                        keybindings=[
                            ('k2', 1),
                        ],
                        namespace='n2')),
                ],
                namespace='n1',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/n1:C1.k1="/n2:C2.k2=1"',
        ),
        None, None, True
    ),
    (
        "reference key that has an int key (normal association), "
        "canonical format",
        dict(
            obj=CIMInstanceName(
                classname='C1',
                keybindings=[
                    ('K1', CIMInstanceName(
                        classname='C2',
                        keybindings=[
                            ('K2', 1),
                        ],
                        namespace='N2')),
                ],
                namespace='N1',
                host=None),
            kwargs=dict(
                format='canonical',
            ),
            exp_uri='/n1:c1.k1="/n2:c2.k2=1"',
        ),
        None, None, True
    ),
    (
        "reference key that has a string key (normal association)",
        dict(
            obj=CIMInstanceName(
                classname='C1',
                keybindings=[
                    ('k1', CIMInstanceName(
                        classname='C2',
                        keybindings=[
                            ('k2', 'v2'),
                        ],
                        namespace='n2')),
                ],
                namespace='n1',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri=r'/n1:C1.k1="/n2:C2.k2=\"v2\""',
        ),
        None, None, True
    ),
    (
        "reference key to instance that has no keys",
        dict(
            obj=CIMInstanceName(
                classname='C1',
                keybindings=[
                    ('k1', CIMInstanceName(
                        classname='C2',
                        keybindings=[],
                        namespace='n2')),
                ],
                namespace='n1',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri=r'/n1:C1.k1="/n2:C2"',
        ),
        None, MissingKeybindingsWarning, True
    ),
    (
        "double nested reference to int key (association to association)",
        dict(
            obj=CIMInstanceName(
                classname='C1',
                keybindings=[
                    ('k1', CIMInstanceName(
                        classname='C2',
                        keybindings=[
                            ('k2', CIMInstanceName(
                                classname='C3',
                                keybindings=[
                                    ('k3', 3),
                                ],
                                namespace='n3')),
                        ],
                        namespace='n2')),
                ],
                namespace='n1',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri=r'/n1:C1.k1="/n2:C2.k2=\"/n3:C3.k3=3\""',
        ),
        None, None, True
    ),
    (
        "double nested reference to string key (association to "
        "association)",
        dict(
            obj=CIMInstanceName(
                classname='C1',
                keybindings=[
                    ('k1', CIMInstanceName(
                        classname='C2',
                        keybindings=[
                            ('k2', CIMInstanceName(
                                classname='C3',
                                keybindings=[
                                    ('k3', 'v3'),
                                ],
                                namespace='n3')),
                        ],
                        namespace='n2')),
                ],
                namespace='n1',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri=r'/n1:C1.k1="/n2:C2.k2=\"/n3:C3.k3=\\\"v3\\\"\""',
        ),
        None, None, True
    ),
    (
        "Just classname, no namespace, host, or keys",
        dict(
            obj=CIMInstanceName(
                classname='C1',
                keybindings=[],
                namespace=None,
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri=r'/:C1',
        ),
        None, MissingKeybindingsWarning, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCENAME_TO_WBEM_URI)
@simplified_test_function
@log_entry_exit
def test_CIMInstanceName_to_wbem_uri(testcase, obj, kwargs, exp_uri):
    """
    Test function for CIMInstanceName.to_wbem_uri()
    """

    # The code to be tested
    uri = obj.to_wbem_uri(**kwargs)

    # 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(uri, str)
    assert uri == exp_uri


TESTCASES_CIMINSTANCENAME_TO_WBEM_URI_SPECIAL_KB = [

    # Testcases for CIMInstanceName.to_wbem_uri(), where the value of an
    # existing keybinding is set to a special value before the test.

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: CIMInstanceName object to be tested.
    #   * kb_name: Name of the keybinding to be set.
    #   * kb_value: New value for the keybinding to be set.
    #   * exp_uri: Expected WBEM URI string, or None if failed.
    # * 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

    (
        "Keybinding with invalid type CIMParameter",
        dict(
            obj=CIMInstanceName('CIM_Foo', keybindings=[('K1', 'V1')]),
            kb_name='K1',
            kb_value=CIMParameter('C2', type='string'),  # invalid type
            exp_uri=None,
        ),
        TypeError, None, True
    ),
    (
        "Keybinding with binary string",
        # Note: This testcase cannot be specified in the standard testcases
        # for to_wbem_uri(), because CIMInstanceName.__init__() ensures
        # that the keybinding values get converted to unicode. The
        # to_wbem_uri() method still needs to handle this case because the
        # keybindng values can be set via the mutable dictionary.
        dict(
            obj=CIMInstanceName('CIM_Foo', keybindings=[('K1', 'V1')]),
            kb_name='K1',
            kb_value=b'V2',
            exp_uri='/:CIM_Foo.K1="V2"',
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCENAME_TO_WBEM_URI_SPECIAL_KB)
@simplified_test_function
@log_entry_exit
def test_CIMInstanceName_to_wbem_uri_special_kb(
        testcase, obj, kb_name, kb_value, exp_uri):
    """
    Test function for CIMInstanceName.to_wbem_uri(), where the value of an
    existing keybinding is set to a special value before the test.
    """

    # Set existing keybinding to the new value. Any invalid types etc. are
    # not checked, because this simply modifies the (mutable) keybindings
    # dictionary.
    obj.keybindings[kb_name] = kb_value
    assert obj.keybindings[kb_name] == kb_value

    # The code to be tested
    uri = obj.to_wbem_uri()

    # 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(uri, str)
    assert uri == exp_uri


TESTCASES_CIMINSTANCENAME_STR = [

    # Testcases for CIMInstanceName.__str__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * attrs: Dict of all attributes for CIMInstanceName.
    #   * exp_uri: Expected WBEM URI string, or None if failed.
    # * 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

    (
        "all components, historical format",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
            exp_uri='//10.11.12.13:5989/root/cimv2:CIM_Foo.k1="v1"',
        ),
        None, None, True
    ),
    (
        "no authority, historical format",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace='root/cimv2',
                host=None),
            exp_uri='root/cimv2:CIM_Foo.k1="v1"',
        ),
        None, None, True
    ),
    (
        "local WBEM URI with only class name, historical format",
        dict(
            obj=CIMInstanceName(
                classname='CIM_Foo',
                keybindings=NocaseDict(k1='v1'),
                namespace=None,
                host=None),
            exp_uri='CIM_Foo.k1="v1"',
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCENAME_STR)
@simplified_test_function
@log_entry_exit
def test_CIMInstanceName_str(testcase, obj, exp_uri):
    """
    Test function for CIMInstanceName.__str__()
    """

    # The code to be tested
    uri = obj.__str__()  # pylint: disable=unnecessary-dunder-call

    # 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(uri, str)
    assert uri == exp_uri


TESTCASES_CIMINSTANCE_INIT = [

    # Testcases for CIMInstance.__init__()

    # 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 CIMInstance().
    #   * init_kwargs: Dict of keyword arguments to CIMInstance().
    #   * exp_attrs: Dict of expected attributes of resulting 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

    # Order of positional arguments
    (
        "Verify order of positional arguments",
        dict(
            init_args=[
                'CIM_Foo',
                [
                    CIMProperty('Prop1', value=True),
                ],
                [
                    CIMQualifier('Qual1', value=True),
                ],
                CIMInstanceName('CIM_Foo'),
            ],
            init_kwargs={},
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('Prop1', CIMProperty('Prop1', value=True)),
                ]),
                qualifiers=NocaseDict([
                    ('Qual1', CIMQualifier('Qual1', value=True)),
                ]),
                path=CIMInstanceName('CIM_Foo'),
            )
        ),
        None, None, True
    ),

    # Classname tests
    (
        "Verify that bytes classname is converted to unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname=b'CIM_Foo',
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
            )
        ),
        None, None, True
    ),
    (
        "Verify that unicode classname remains unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
            )
        ),
        None, None, True
    ),

    # Property tests
    (
        "Verify property order preservation with list of CIMProperty",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=[
                    CIMProperty('K1', value='Ham'),
                    CIMProperty('K2', value='Cheese'),
                ]
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('K1', CIMProperty('K1', value='Ham')),
                    ('K2', CIMProperty('K2', value='Cheese')),
                ])
            )
        ),
        None, None, True
    ),
    (
        "Verify property order preservation with OrderedDict",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=OrderedDict([
                    ('K1', 'Ham'),
                    ('K2', 'Cheese'),
                ])
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('K1', CIMProperty('K1', value='Ham')),
                    ('K2', CIMProperty('K2', value='Cheese')),
                ])
            )
        ),
        None, None, True
    ),
    (
        "Verify property order preservation with list of tuple(key,val)",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=[
                    ('K1', 'Ham'),
                    ('K2', 'Cheese'),
                ]
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('K1', CIMProperty('K1', value='Ham')),
                    ('K2', CIMProperty('K2', value='Cheese')),
                ])
            )
        ),
        None, None, True
    ),
    (
        "Verify property with bytes string value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(
                    K1=b'Ham',
                )
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('K1', CIMProperty('K1', value='Ham')),
                ]),
            )
        ),
        None, None, True
    ),
    (
        "Verify property with unicode string value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                # lower case a umlaut
                properties=dict(
                    K1='H\u00E4m',
                )
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('K1', CIMProperty('K1', value='H\u00E4m')),
                ]),
            )
        ),
        None, None, True
    ),
    (
        "Verify property with boolean True value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(K1=True)
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('K1', CIMProperty('K1', value=True)),
                ]),
            )
        ),
        None, None, True
    ),
    (
        "Verify property with boolean False value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(K1=False),
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('K1', CIMProperty('K1', value=False)),
                ]),
            )
        ),
        None, None, True
    ),
    (
        "Verify property with Uint8 value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(K1=Uint8(42)),
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('K1', CIMProperty('K1', value=Uint8(42))),
                ]),
            )
        ),
        None, None, True
    ),
    (
        "Verify property with Uint16 value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(K1=Uint16(4216)),
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('K1', CIMProperty('K1', value=Uint16(4216))),
                ]),
            )
        ),
        None, None, True
    ),
    (
        "Verify property with Uint32 value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(K1=Uint32(4232)),
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('K1', CIMProperty('K1', value=Uint32(4232))),
                ]),
            )
        ),
        None, None, True
    ),
    (
        "Verify property with Uint64 value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(K1=Uint64(4264)),
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('K1', CIMProperty('K1', value=Uint64(4264))),
                ]),
            )
        ),
        None, None, True
    ),
    (
        "Verify property with Sint8 value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(K1=Sint8(-42)),
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('K1', CIMProperty('K1', value=Sint8(-42))),
                ]),
            )
        ),
        None, None, True
    ),
    (
        "Verify property with Sint16 value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(K1=Sint16(-4216)),
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('K1', CIMProperty('K1', value=Sint16(-4216))),
                ]),
            )
        ),
        None, None, True
    ),
    (
        "Verify property with Sint32 value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(K1=Sint32(-4232)),
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('K1', CIMProperty('K1', value=Sint32(-4232))),
                ]),
            )
        ),
        None, None, True
    ),
    (
        "Verify property with Sint64 value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(K1=Sint64(-4264)),
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('K1', CIMProperty('K1', value=Sint64(-4264))),
                ]),
            )
        ),
        None, None, True
    ),
    (
        "Verify property with Real32 value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(K1=Real32(-42.32)),
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('K1', CIMProperty('K1', value=Real32(-42.32))),
                ]),
            )
        ),
        None, None, True
    ),
    (
        "Verify property with Real64 value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(K1=Real64(-42.64)),
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('K1', CIMProperty('K1', value=Real64(-42.64))),
                ]),
            )
        ),
        None, None, True
    ),
    (
        "Verify property with datetime value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(K1=DATETIME1_OBJ),
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('K1', CIMProperty('K1', value=DATETIME1_OBJ)),
                ]),
            )
        ),
        None, None, True
    ),
    (
        "Verify property with timedelta value",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(K1=TIMEDELTA1_OBJ),
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('K1', CIMProperty('K1', value=TIMEDELTA1_OBJ)),
                ]),
            )
        ),
        None, None, True
    ),
    (
        "Verify property with CIMProperty (of arbitrary type and value)",
        # Note: The full range of possible input values and types for
        # CIMProperty objects is tested in CIMProperty testcases.
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(K1=CIMProperty('K1', value='Ham')),
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('K1', CIMProperty('K1', value='Ham')),
                ]),
            )
        ),
        None, None, True
    ),
    (
        "Verify two properties",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=OrderedDict([
                    ('K1', 'Ham'),
                    ('K2', Uint8(42)),
                ]),
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('K1', CIMProperty('K1', value='Ham')),
                    ('K2', CIMProperty('K2', value=Uint8(42))),
                ]),
            )
        ),
        None, None, True
    ),
    (
        "Verify case insensitivity of property names",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(Key1='Ham'),
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('kEY1', CIMProperty('kEY1', value='Ham')),
                ]),
            )
        ),
        None, None, True
    ),

    # Qualifiers tests
    (
        "Verify qualifiers order preservation with list of CIMQualifier",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                qualifiers=[
                    CIMQualifier('Q1', value='Ham'),
                    CIMQualifier('Q2', value='Cheese'),
                ]
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                qualifiers=NocaseDict([
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                    ('Q2', CIMQualifier('Q2', value='Cheese')),
                ])
            )
        ),
        None, None, True
    ),
    (
        "Verify qualifiers order preservation with OrderedDict",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                qualifiers=OrderedDict([
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                    ('Q2', CIMQualifier('Q2', value='Cheese')),
                ])
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                qualifiers=NocaseDict([
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                    ('Q2', CIMQualifier('Q2', value='Cheese')),
                ])
            )
        ),
        None, None, True
    ),
    (
        "Verify qualifiers order preservation with list of tuple(key,val)",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                qualifiers=[
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                    ('Q2', CIMQualifier('Q2', value='Cheese')),
                ],
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                qualifiers=NocaseDict([
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                    ('Q2', CIMQualifier('Q2', value='Cheese')),
                ]),
            )
        ),
        None, None, True
    ),
    (
        "Verify qualifier with CIMQualifier (of arbitrary type and value)",
        # Note: The full range of possible input values and types for
        # CIMQualifier objects is tested in CIMQualifier testcases.

        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                qualifiers=[CIMQualifier('Q1', value='Ham')],
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                qualifiers=NocaseDict([
                    CIMQualifier('Q1', value='Ham'),
                ]),
            )
        ),
        None, None, True
    ),
    (
        "Verify that equality of instances does not depend on order of "
        "qualifiers",

        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                qualifiers=[
                    CIMQualifier('Q1', value='Ham'),
                    CIMQualifier('Q2', value=True),
                ],
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                qualifiers=NocaseDict([
                    CIMQualifier('Q2', value=True),
                    CIMQualifier('Q1', value='Ham'),
                ]),
            )
        ),
        None, None, True
    ),

    # Path tests
    (
        "Verify path without corresponding key property",

        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                path=CIMInstanceName('CIM_Foo', keybindings=dict(K1='Key1')),
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                path=CIMInstanceName(
                    'CIM_Foo',
                    keybindings=NocaseDict([('K1', 'Key1')])
                ),
            )
        ),
        None, None, True
    ),
    (
        "Verify the keybindings in path with corresponding key property "
        "are set to key property value",
        # Note: The full set of tests for this kind of updating is in
        # CIMInstancePropertyList.

        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(K1='Ham'),
                path=CIMInstanceName('CIM_Foo', keybindings=dict(K1='Key1')),
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    CIMProperty('K1', value='Ham'),
                ]),
                path=CIMInstanceName(
                    'CIM_Foo',
                    keybindings=NocaseDict([
                        ('K1', 'Ham'),  # keybinding has been updated
                    ])
                ),
            )
        ),
        None, DeprecationWarning, True
    ),

    # Exception testcases
    (
        "Verify that classname None fails (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname=None
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that property with name None fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties={None: 'abc'}
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that property with a value of some other unsupported "
        "object type (e.g. exception type) fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(K1=TypeError)
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that property with inconsistent name fails (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(P1=CIMProperty('P1_X', 'abc')),
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that property with int value fails with ValueError "
        "(since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(P1=42),
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that property with float value fails with ValueError "
        "(since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(P1=42.1),
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that properties with item of invalid type fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=[
                    'xxx_invalid_type',
                ],
            ),
            exp_attrs=None
        ),
        TypeError, None, True
    ),
    (
        "Verify that qualifier with name None fails (with ValueError "
        "since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                qualifiers={None: 'abc'},
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that qualifier with inconsistent name fails "
        "(since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                qualifiers=dict(Q1=CIMQualifier('Q1_X', 'abc')),
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that qualifiers with item of invalid type fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                qualifiers=[
                    'xxx_invalid_type',
                ],
            ),
            exp_attrs=None
        ),
        TypeError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCE_INIT)
@simplified_test_function
@log_entry_exit
def test_CIMInstance_init(testcase, init_args, init_kwargs, exp_attrs):
    """
    Test function for CIMInstance.__init__()
    """

    # The code to be tested
    obj = CIMInstance(*init_args, **init_kwargs)

    assert not hasattr(obj, '__dict__')

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

    exp_classname = exp_attrs['classname']
    assert obj.classname == exp_classname
    assert isinstance(obj.classname, type(exp_classname))

    exp_properties = exp_attrs.get('properties', NocaseDict())
    assert obj.properties == exp_properties
    assert isinstance(obj.properties, type(exp_properties))

    exp_qualifiers = exp_attrs.get('qualifiers', NocaseDict())
    assert obj.qualifiers == exp_qualifiers
    assert isinstance(obj.qualifiers, type(exp_qualifiers))

    exp_path = exp_attrs.get('path', None)
    assert obj.path == exp_path
    assert isinstance(obj.path, type(exp_path))


TESTCASES_CIMINSTANCE_COPY = [

    # Testcases for CIMInstance.copy()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj_kwargs: Dict of init kwargs for original CIMInstance.
    # * 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

    (
        "All attributes set",
        dict(
            obj_kwargs=dict(
                classname='CIM_Foo',
                properties=[
                    CIMProperty('Name', value='Foo'),
                ],
                qualifiers=[
                    CIMQualifier('Key', value=True),
                ],
                path=CIMInstanceName('CIM_Foo', keybindings={'Name': 'Foo'}),
            )
        ),
        None, None, True
    ),
    (
        "No Properties",
        dict(
            obj_kwargs=dict(
                classname='CIM_Foo',
                properties=None,
                qualifiers=[
                    CIMQualifier('Key', value=True),
                ],
                path=CIMInstanceName('CIM_Foo', keybindings={'Name': 'Foo'}),
            )
        ),
        None, None, True
    ),
    (
        "No Qualifiers",
        dict(
            obj_kwargs=dict(
                classname='CIM_Foo',
                properties=[
                    CIMProperty('Name', value='Foo'),
                ],
                qualifiers=None,
                path=CIMInstanceName('CIM_Foo', keybindings={'Name': 'Foo'}),
            )
        ),
        None, None, True
    ),
    (
        "No Path",
        dict(
            obj_kwargs=dict(
                classname='CIM_Foo',
                properties=[
                    CIMProperty('Name', value='Foo'),
                ],
                qualifiers=[
                    CIMQualifier('Key', value=True),
                ],
                path=None
            )
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCE_COPY)
@simplified_test_function
@log_entry_exit
def test_CIMInstance_copy(testcase, obj_kwargs):
    """
    Test function for CIMInstance.copy()
    """

    obj1 = CIMInstance(**obj_kwargs)

    # The code to be tested
    obj2 = obj1.copy()

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

    # Verify that the copy is equal to the original (performs deep equality)
    assert obj2 == obj1

    # Verify that the copy is a different object
    assert id(obj2) != id(obj1)

    # Verify that the mutable child objects are different objects
    if obj1.properties is not None:
        assert id(obj2.properties) != id(obj1.properties)
    if obj1.qualifiers is not None:
        assert id(obj2.qualifiers) != id(obj1.qualifiers)
    if obj1.path is not None:
        assert id(obj2.path) != id(obj1.path)

    # Verify that the copy can be modified and the original remains unchanged

    obj1_classname = obj1.classname
    obj2.classname = 'SomeNewClassname'
    assert obj1.classname == obj1_classname

    obj1_properties = obj1.properties
    obj2.properties = [CIMProperty('SomeNewProperty', value='somevalue')]
    assert obj1.properties == obj1_properties

    obj1_qualifiers = obj1.qualifiers
    obj2.qualifiers = [CIMQualifier('SomeNewQualifier', value=True)]
    assert obj1.qualifiers == obj1_qualifiers

    obj1_path = obj1.path
    obj2.path = CIMInstanceName('SomeNewClassname', {'SomeNewKey': 'val'})
    assert obj1.path == obj1_path


CIMINSTANCE_SETATTR_I1_KWARGS = dict(
    classname='C1',
    properties=dict(P1=CIMProperty('P1', False)),
    qualifiers=dict(Q1=CIMQualifier('Q1', False)),
    path=CIMInstanceName('CIM_Foo', dict(P1=False)),
)

TESTCASES_CIMINSTANCE_SETATTR = [

    # Testcases for CIMInstance set attribute

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj_kwargs: Dict of init kwargs for CIMInstance.
    #   * item: Name of CIMInstance attr to set, or tuple
    #     (item, key) to set a single item in an attr that is a dict.
    #   * new_value: New value to set.
    #   * exp_attrs: Dict of expected attributes, for validation.
    # * 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

    # Tests that set the classname attribute
    (
        "Set classname to different string",
        dict(
            obj_kwargs=CIMINSTANCE_SETATTR_I1_KWARGS,
            item='classname',
            new_value='CIM_Bar',
            exp_attrs=dict(
                classname='CIM_Bar',
            ),
        ),
        None, None, True
    ),
    (
        "Set classname to None (raises ValueError since 0.12)",
        dict(
            obj_kwargs=CIMINSTANCE_SETATTR_I1_KWARGS,
            item='classname',
            new_value=None,
            exp_attrs=None,
        ),
        ValueError, None, True
    ),

    # Tests that set the properties attribute
    (
        "Set properties to new dict with CIMProperty with correct name",
        dict(
            obj_kwargs=CIMINSTANCE_SETATTR_I1_KWARGS,
            item='properties',
            new_value=dict(P2=CIMProperty('P2', True)),
            exp_attrs=dict(
                properties=dict(
                    P2=CIMProperty('P2', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set properties to new dict with CIMProperty with incorrect name",
        dict(
            obj_kwargs=CIMINSTANCE_SETATTR_I1_KWARGS,
            item='properties',
            new_value=dict(P2=CIMProperty('P2x', True)),
            exp_attrs=None,
        ),
        # raises ValueError since 0.12
        ValueError, None, True
    ),
    (
        "Set properties to new dict with CIMProperty with name None",
        dict(
            obj_kwargs=CIMINSTANCE_SETATTR_I1_KWARGS,
            item='properties',
            # Name of CIMProperty object is not None, to get over that check.
            new_value=dict([(None, CIMProperty('Pnone', True))]),
            exp_attrs=dict(
                properties=dict([
                    ('P1', CIMProperty('P1', False)),
                    (None, CIMProperty('Pnone', True)),
                ]),
            ),
        ),
        # raises ValueError since 0.12
        ValueError, None, True
    ),
    (
        "Set properties to None",
        dict(
            obj_kwargs=CIMINSTANCE_SETATTR_I1_KWARGS,
            item='properties',
            new_value=None,
            exp_attrs=dict(
                properties={},
            ),
        ),
        None, None, True
    ),
    (
        "Set existing property to new CIMProperty with correct name",
        dict(
            obj_kwargs=CIMINSTANCE_SETATTR_I1_KWARGS,
            item=('properties', 'P1'),
            new_value=CIMProperty('P1', True),
            exp_attrs=dict(
                properties=dict(
                    P1=CIMProperty('P1', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set existing property to new CIMProperty with incorrect name",
        dict(
            obj_kwargs=CIMINSTANCE_SETATTR_I1_KWARGS,
            item=('properties', 'P1'),
            new_value=CIMProperty('P1x', True),
            # No checking for correct name since this sets an item in a normal
            # NocaseDict:
            exp_attrs=dict(
                properties=dict(
                    P1=CIMProperty('P1x', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new property to new CIMProperty with correct name",
        dict(
            obj_kwargs=CIMINSTANCE_SETATTR_I1_KWARGS,
            item=('properties', 'P2'),
            new_value=CIMProperty('P2', True),
            exp_attrs=dict(
                properties=dict(
                    P1=CIMProperty('P1', False),
                    P2=CIMProperty('P2', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new property to new CIMProperty with incorrect name",
        dict(
            obj_kwargs=CIMINSTANCE_SETATTR_I1_KWARGS,
            item=('properties', 'P2'),
            new_value=CIMProperty('P2x', True),
            # No checking for correct name since this sets an item in a normal
            # NocaseDict:
            exp_attrs=dict(
                properties=dict(
                    P1=CIMProperty('P1', False),
                    P2=CIMProperty('P2x', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new property with name None",
        dict(
            obj_kwargs=CIMINSTANCE_SETATTR_I1_KWARGS,
            item=('properties', None),
            # Name of CIMProperty object is not None, to get over that check.
            new_value=CIMProperty('Pnone', True),
            exp_attrs=None,
        ),
        ValueError if version_info >= (1, 0) else TypeError, None, True
        # None as key in NocaseDict
    ),

    # Tests that set the qualifiers attribute
    (
        "Set qualifiers to new dict with CIMQualifier with correct name",
        dict(
            obj_kwargs=CIMINSTANCE_SETATTR_I1_KWARGS,
            item='qualifiers',
            new_value=dict(Q2=CIMQualifier('Q2', True)),
            exp_attrs=dict(
                qualifiers=dict(
                    Q2=CIMQualifier('Q2', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set qualifiers to new dict with CIMQualifier with incorrect name",
        dict(
            obj_kwargs=CIMINSTANCE_SETATTR_I1_KWARGS,
            item='qualifiers',
            new_value=dict(Q2=CIMQualifier('Q2x', True)),
            exp_attrs=None,
        ),
        # raises ValueError since 0.12
        ValueError, None, True
    ),
    (
        "Set qualifiers to new dict with CIMQualifier with name None",
        dict(
            obj_kwargs=CIMINSTANCE_SETATTR_I1_KWARGS,
            item='qualifiers',
            # Name of CIMQualifier object is not None, to get over that check.
            new_value=dict([(None, CIMQualifier('Qnone', True))]),
            exp_attrs=dict(
                qualifiers=dict([
                    ('Q1', CIMQualifier('Q1', False)),
                    (None, CIMQualifier('Qnone', True)),
                ]),
            ),
        ),
        # raises ValueError since 0.12
        ValueError, None, True
    ),
    (
        "Set qualifiers to new dict with simple value",
        dict(
            obj_kwargs=CIMINSTANCE_SETATTR_I1_KWARGS,
            item='qualifiers',
            new_value=dict(Q1=True),
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1', True)
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set qualifiers to None",
        dict(
            obj_kwargs=CIMINSTANCE_SETATTR_I1_KWARGS,
            item='qualifiers',
            new_value=None,
            exp_attrs=dict(
                qualifiers={},
            ),
        ),
        None, None, True
    ),
    (
        "Set existing qualifier to new CIMQualifier with correct name",
        dict(
            obj_kwargs=CIMINSTANCE_SETATTR_I1_KWARGS,
            item=('qualifiers', 'Q1'),
            new_value=CIMQualifier('Q1', True),
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set existing qualifier to new CIMQualifier with incorrect name",
        dict(
            obj_kwargs=CIMINSTANCE_SETATTR_I1_KWARGS,
            item=('qualifiers', 'Q1'),
            new_value=CIMQualifier('Q1x', True),
            # No checking for correct name since this sets an item in a normal
            # NocaseDict:
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1x', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new qualifier to new CIMQualifier with correct name",
        dict(
            obj_kwargs=CIMINSTANCE_SETATTR_I1_KWARGS,
            item=('qualifiers', 'Q2'),
            new_value=CIMQualifier('Q2', True),
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1', False),
                    Q2=CIMQualifier('Q2', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new qualifier to new CIMQualifier with incorrect name",
        dict(
            obj_kwargs=CIMINSTANCE_SETATTR_I1_KWARGS,
            item=('qualifiers', 'Q2'),
            new_value=CIMQualifier('Q2x', True),
            # No checking for correct name since this sets an item in a normal
            # NocaseDict:
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1', False),
                    Q2=CIMQualifier('Q2x', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new qualifier with name None",
        dict(
            obj_kwargs=CIMINSTANCE_SETATTR_I1_KWARGS,
            item=('qualifiers', None),
            # Name of CIMQualifier object is not None, to get over that check.
            new_value=CIMQualifier('Qnone', True),
            exp_attrs=None,
        ),
        ValueError if version_info >= (1, 0) else TypeError, None, True
        # None as key in NocaseDict
    ),

    # Tests that set the path attribute
    (
        "Set path to new CIMInstanceName",
        dict(
            obj_kwargs=CIMINSTANCE_SETATTR_I1_KWARGS,
            item='path',
            new_value=CIMINSTANCENAME_C1_OBJ,
            exp_attrs=dict(
                path=CIMINSTANCENAME_C1_OBJ,
            ),
        ),
        None, None, True
    ),
    (
        "Set path to None",
        dict(
            obj_kwargs=CIMINSTANCE_SETATTR_I1_KWARGS,
            item='path',
            new_value=None,
            exp_attrs=dict(
                path=None,
            ),
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCE_SETATTR)
@simplified_test_function
@log_entry_exit
def test_CIMInstance_setattr(
        testcase, obj_kwargs, item, new_value, exp_attrs):
    """
    Test function for CIMInstance set attribute
    """

    obj = CIMInstance(**obj_kwargs)

    if isinstance(item, tuple):
        attr_name, attr_key = item
        attr_dict = getattr(obj, attr_name)

        # The code to be tested (for setting a single dict item)
        attr_dict[attr_key] = new_value

    else:
        attr_name = item

        # The code to be tested (for setting the entire attribute)
        setattr(obj, attr_name, new_value)

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

    for attr_name in exp_attrs:
        exp_value = exp_attrs[attr_name]
        value = getattr(obj, attr_name)
        assert value == exp_value


TESTCASES_CIMINSTANCE_UPDATE_PROPERTY = [

    # Testcases for CIMInstance update property

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj_kwargs: Dict of init kwargs for CIMInstance.
    #   * prop_name: Name of property to update.
    #   * new_value: New value to set.
    #   * exp_attrs: Dict of expected attributes, for validation.
    # * 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

    (
        "Update existing property that also exists in keybindings",
        dict(
            obj_kwargs=dict(
                classname='C1',
                properties=dict(
                    K1=CIMProperty('K1', 'V1'),
                ),
                path=CIMInstanceName(
                    'C1',
                    keybindings=dict(K1='V1')
                ),
            ),
            prop_name='K1',
            new_value='V2',
            exp_attrs=dict(
                properties=dict(
                    K1=CIMProperty('K1', 'V2'),
                ),
                path=CIMInstanceName(
                    'C1',
                    keybindings=dict(K1='V2')  # keybinding has been updated
                ),
            ),
        ),
        None, DeprecationWarning, True
    ),
    (
        "Update existing property that does not exist in keybindings",
        dict(
            obj_kwargs=dict(
                classname='C1',
                properties=dict(
                    P1=CIMProperty('P1', 'V1'),
                ),
                path=CIMInstanceName(
                    'C1',
                    keybindings=dict(K1='V1')
                ),
            ),
            prop_name='P1',
            new_value='V2',
            exp_attrs=dict(
                properties=dict(
                    P1=CIMProperty('P1', 'V2'),
                ),
                path=CIMInstanceName(
                    'C1',
                    keybindings=dict(K1='V1')  # not added as keybinding
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Update non-existing property that exists in keybindings",
        dict(
            obj_kwargs=dict(
                classname='C1',
                properties={},
                path=CIMInstanceName(
                    'C1',
                    keybindings=dict(K1='V1')
                ),
            ),
            prop_name='K1',
            new_value='V2',
            exp_attrs=dict(
                properties=dict(
                    K1=CIMProperty('K1', 'V2'),  # added
                ),
                path=CIMInstanceName(
                    'C1',
                    keybindings=dict(K1='V2')  # keybinding has been updated
                ),
            ),
        ),
        None, DeprecationWarning, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCE_UPDATE_PROPERTY)
@simplified_test_function
@log_entry_exit
def test_CIMInstance_update_property(
        testcase, obj_kwargs, prop_name, new_value, exp_attrs):
    """
    Test function for CIMInstance update property
    """

    obj = CIMInstance(**obj_kwargs)

    # The code to be tested
    obj[prop_name] = new_value

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

    for attr_name in exp_attrs:
        exp_value = exp_attrs[attr_name]
        value = getattr(obj, attr_name)
        assert value == exp_value


TESTCASES_CIMINSTANCE_UPDATE_EXISTING = [

    # Testcases for CIMInstance.update_existing()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj_kwargs: Dict of init kwargs for CIMInstance.
    #   * func_args: Positional args for CIMInstance.update_existing()
    #   * func_kwargs: Keyword args for CIMInstance.update_existing()
    #   * exp_attrs: Dict of expected attributes, for validation.
    # * 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

    (
        "No arguments",
        dict(
            obj_kwargs=dict(
                classname='C1',
                properties=dict(
                    P1=CIMProperty('P1', 'V1'),
                ),
            ),
            func_args=[],
            func_kwargs={},
            exp_attrs=dict(
                properties=dict(
                    P1=CIMProperty('P1', 'V1'),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Update existing property via positional arg as tuple",
        dict(
            obj_kwargs=dict(
                classname='C1',
                properties=dict(
                    P1=CIMProperty('P1', 'V1'),
                ),
            ),
            func_args=[
                [('P1', 'V1new')],
            ],
            func_kwargs={},
            exp_attrs=dict(
                properties=dict(
                    P1=CIMProperty('P1', 'V1new'),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Update existing property via positional arg as CIMInstanceName",
        dict(
            obj_kwargs=dict(
                classname='C1',
                properties=dict(
                    P1=CIMProperty('P1', 'V1'),
                ),
            ),
            func_args=[
                CIMInstanceName('C2', keybindings=dict(P1='V1new')),
            ],
            func_kwargs={},
            exp_attrs=dict(
                properties=dict(
                    P1=CIMProperty('P1', 'V1new'),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Update existing property via keyword arg",
        dict(
            obj_kwargs=dict(
                classname='C1',
                properties=dict(
                    P1=CIMProperty('P1', 'V1'),
                ),
            ),
            func_args=[],
            func_kwargs=dict(
                P1='V1new',
            ),
            exp_attrs=dict(
                properties=dict(
                    P1=CIMProperty('P1', 'V1new'),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Update non-existing property via positional arg as tuple",
        dict(
            obj_kwargs=dict(
                classname='C1',
                properties=dict(
                    P1=CIMProperty('P1', 'V1'),
                ),
            ),
            func_args=[
                [('P2', 'V2new')],
            ],
            func_kwargs={},
            exp_attrs=dict(
                properties=dict(
                    P1=CIMProperty('P1', 'V1'),  # P2 ignored
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Update non-existing property via positional arg as CIMInstanceName",
        dict(
            obj_kwargs=dict(
                classname='C1',
                properties=dict(
                    P1=CIMProperty('P1', 'V1'),
                ),
            ),
            func_args=[
                CIMInstanceName('C2', keybindings=dict(P2='V2new')),
            ],
            func_kwargs={},
            exp_attrs=dict(
                properties=dict(
                    P1=CIMProperty('P1', 'V1'),  # P2 ignored
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Update non-existing property via keyword arg",
        dict(
            obj_kwargs=dict(
                classname='C1',
                properties=dict(
                    P1=CIMProperty('P1', 'V1'),
                ),
            ),
            func_args=[],
            func_kwargs=dict(
                P2='V2new',
            ),
            exp_attrs=dict(
                properties=dict(
                    P1=CIMProperty('P1', 'V1'),  # P2 ignored
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Update one existing / one non-existing property via one positional "
        "arg as tuples",
        dict(
            obj_kwargs=dict(
                classname='C1',
                properties=dict(
                    P1=CIMProperty('P1', 'V1'),
                ),
            ),
            func_args=[
                [('P1', 'V1new'), ('P2', 'V2new')],
            ],
            func_kwargs={},
            exp_attrs=dict(
                properties=dict(
                    P1=CIMProperty('P1', 'V1new'),  # P2 ignored
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Update one existing / one non-existing property via two positional "
        "args as tuples",
        dict(
            obj_kwargs=dict(
                classname='C1',
                properties=dict(
                    P1=CIMProperty('P1', 'V1'),
                ),
            ),
            func_args=[
                [('P1', 'V1new')],
                [('P2', 'V2new')],
            ],
            func_kwargs={},
            exp_attrs=dict(
                properties=dict(
                    P1=CIMProperty('P1', 'V1new'),  # P2 ignored
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Update one existing / one non-existing property via positional arg "
        "as CIMInstanceName",
        dict(
            obj_kwargs=dict(
                classname='C1',
                properties=dict(
                    P1=CIMProperty('P1', 'V1'),
                ),
            ),
            func_args=[
                CIMInstanceName('C2', keybindings=dict(P1='V1new', P2='V2new'))
            ],
            func_kwargs={},
            exp_attrs=dict(
                properties=dict(
                    P1=CIMProperty('P1', 'V1new'),  # P2 ignored
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Update one existing / one non-existing property via keyword args",
        dict(
            obj_kwargs=dict(
                classname='C1',
                properties=dict(
                    P1=CIMProperty('P1', 'V1'),
                ),
            ),
            func_args=[],
            func_kwargs=dict(
                P1='V1new',
                P2='V2new',
            ),
            exp_attrs=dict(
                properties=dict(
                    P1=CIMProperty('P1', 'V1new'),  # P2 ignored
                ),
            ),
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCE_UPDATE_EXISTING)
@simplified_test_function
@log_entry_exit
def test_CIMInstance_update_existing(
        testcase, obj_kwargs, func_args, func_kwargs, exp_attrs):
    """
    Test function for CIMInstance.update_existing()
    """

    obj = CIMInstance(**obj_kwargs)

    # The code to be tested
    obj.update_existing(*func_args, **func_kwargs)

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

    for attr_name in exp_attrs:
        exp_value = exp_attrs[attr_name]
        value = getattr(obj, attr_name)
        assert value == exp_value


TESTCASES_CIMINSTANCE_HASH_EQ = [

    # Testcases for CIMInstance.__hash__() and __eq__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: CIMInstance object #1 to be tested.
    #   * obj2: CIMInstance object #2 to be tested.
    #   * exp_equal: Expected equality of the object hash values.
    # * 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

    # Classname tests
    (
        "Classname, equal with same lexical case",
        dict(
            obj1=CIMInstance('CIM_Foo'),
            obj2=CIMInstance('CIM_Foo'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Classname, equal with different lexical case",
        dict(
            obj1=CIMInstance('CIM_Foo'),
            obj2=CIMInstance('ciM_foO'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Classname, different",
        dict(
            obj1=CIMInstance('CIM_Foo'),
            obj2=CIMInstance('CIM_Foo_x'),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Path tests
    (
        "Instance paths, equal with same lexical case",
        dict(
            obj1=CIMInstance(
                'CIM_Foo',
                path=CIMInstanceName('CIM_Foo', {'k1': 'v1'}),
            ),
            obj2=CIMInstance(
                'CIM_Foo',
                path=CIMInstanceName('CIM_Foo', {'k1': 'v1'}),
            ),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Instance paths, equal with different lexical case in classname",
        dict(
            obj1=CIMInstance(
                'CIM_Foo',
                path=CIMInstanceName('CIM_Foo', {'k1': 'v1'}),
            ),
            obj2=CIMInstance(
                'CIM_Foo',
                path=CIMInstanceName('CIM_foo', {'k1': 'v1'}),
            ),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Instance paths, different",
        dict(
            obj1=CIMInstance(
                'CIM_Foo',
                path=CIMInstanceName('CIM_Foo', {'k1': 'v1'}),
            ),
            obj2=CIMInstance(
                'CIM_Foo',
                path=CIMInstanceName('CIM_Foo', {'k1': 'v1_x'}),
            ),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Properties tests
    (
        "Matching properties, names with same lexical case",
        dict(
            obj1=CIMInstance('CIM_Foo', properties={'Cheepy': 'Birds'}),
            obj2=CIMInstance('CIM_Foo', properties={'Cheepy': 'Birds'}),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Matching properties, names with different lexical case",
        dict(
            obj1=CIMInstance('CIM_Foo', properties={'Cheepy': 'Birds'}),
            obj2=CIMInstance('CIM_Foo', properties={'cheepy': 'Birds'}),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Matching properties, values None",
        dict(
            obj1=CIMInstance(
                'CIM_Foo',
                properties=[CIMProperty('Cheepy', None, type='string')]
            ),
            obj2=CIMInstance(
                'CIM_Foo',
                properties=[CIMProperty('Cheepy', None, type='string')]
            ),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Non-matching properties, values None and empty string of type string",
        dict(
            obj1=CIMInstance(
                'CIM_Foo',
                properties=[CIMProperty('Cheepy', None, type='string')]
            ),
            obj2=CIMInstance(
                'CIM_Foo',
                properties=[CIMProperty('Cheepy', '', type='string')]
            ),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching properties, values None and 0 of type uint8",
        dict(
            obj1=CIMInstance(
                'CIM_Foo',
                properties=[CIMProperty('Cheepy', None, type='uint8')]
            ),
            obj2=CIMInstance(
                'CIM_Foo',
                properties=[CIMProperty('Cheepy', 0, type='uint8')]
            ),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching properties, one property more",
        dict(
            obj1=CIMInstance('CIM_Foo'),
            obj2=CIMInstance('CIM_Foo', properties={'Cheepy': 'Birds'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching properties, one property less",
        dict(
            obj1=CIMInstance('CIM_Foo', properties={'Cheepy': 'Birds'}),
            obj2=CIMInstance('CIM_Foo'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching properties, different properties",
        dict(
            obj1=CIMInstance('CIM_Foo', properties={'Cheepy': 'Birds'}),
            obj2=CIMInstance('CIM_Foo', properties={'Creepy': 'Ants'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Matching properties, with values that differ in lexical case",
        dict(
            obj1=CIMInstance('CIM_Foo', properties={'Cheepy': 'Birds'}),
            obj2=CIMInstance('CIM_Foo', properties={'Cheepy': 'birds'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Matching properties, with values that is unicode / string",
        dict(
            obj1=CIMInstance('CIM_Foo', properties={'Cheepy': 'Birds'}),
            obj2=CIMInstance('CIM_Foo', properties={'Cheepy': 'Birds'}),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Matching properties with a number of types",
        dict(
            obj1=CIMInstance('CIM_Foo', properties={
                'Name': 'Foo',
                'Boolean': False,
                'Number': Uint8(42),
                'Ref': CIMInstanceName('CIM_Bar'),
            }),
            obj2=CIMInstance('CIM_Foo', properties={
                'Name': 'Foo',
                'Boolean': False,
                'Number': Uint8(42),
                'Ref': CIMInstanceName('CIM_Bar'),
            }),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Property with different types: bool True / string 'TRUE'",
        dict(
            obj1=CIMInstance('CIM_Foo', properties={'Foo': True}),
            obj2=CIMInstance('CIM_Foo', properties={'Foo': 'TRUE'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Property with different types: bool False / string 'FALSE'",
        dict(
            obj1=CIMInstance('CIM_Foo', properties={'Foo': False}),
            obj2=CIMInstance('CIM_Foo', properties={'Foo': 'FALSE'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Native value and CIMProperty of type string",
        dict(
            obj1=CIMInstance(
                'CIM_Foo',
                properties={'Foo': 'string'}
            ),
            obj2=CIMInstance(
                'CIM_Foo',
                properties={'Foo': CIMProperty('Foo', 'string')}
            ),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Native value and CIMProperty of type uint8",
        dict(
            obj1=CIMInstance(
                'CIM_Foo',
                properties={'Foo': Uint8(42)}
            ),
            obj2=CIMInstance(
                'CIM_Foo',
                properties={'Foo': CIMProperty('Foo', Uint8(42))}
            ),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Native value and CIMProperty of type uint8 array",
        dict(
            obj1=CIMInstance(
                'CIM_Foo',
                properties={'Foo': [Uint8(1), Uint8(2)]}
            ),
            obj2=CIMInstance(
                'CIM_Foo',
                properties={'Foo': CIMProperty('Foo', [Uint8(1), Uint8(2)])}
            ),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Native value and CIMProperty of type uint8 array",
        dict(
            obj1=CIMInstance(
                'CIM_Foo',
                properties={'Foo': CIMInstanceName('CIM_Bar')}
            ),
            obj2=CIMInstance(
                'CIM_Foo',
                properties={'Foo': CIMProperty('Foo',
                                               CIMInstanceName('CIM_Bar'))}
            ),
            exp_equal=True,
        ),
        None, None, True
    ),

    # Qualifiers tests
    (
        "Matching qualifiers, qualifier names with same lexical case",
        dict(
            obj1=CIMInstance('CIM_Foo',
                             qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMInstance('CIM_Foo',
                             qualifiers={'Cheepy': 'Birds'}),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Matching qualifiers, qualifier names with different lexical case",
        dict(
            obj1=CIMInstance('CIM_Foo',
                             qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMInstance('CIM_Foo',
                             qualifiers={'cheepy': 'Birds'}),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Non-matching qualifiers, one qualifier more",
        dict(
            obj1=CIMInstance('CIM_Foo'),
            obj2=CIMInstance('CIM_Foo',
                             qualifiers={'Cheepy': 'Birds'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching qualifiers, one qualifier less",
        dict(
            obj1=CIMInstance('CIM_Foo'),
            obj2=CIMInstance('CIM_Foo',
                             qualifiers={'Cheepy': 'Birds'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching qualifiers, different qualifiers",
        dict(
            obj1=CIMInstance('CIM_Foo',
                             qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMInstance('CIM_Foo',
                             qualifiers={'Creepy': 'Ants'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Matching qualifiers, with values that differ in lexical case",
        dict(
            obj1=CIMInstance('CIM_Foo',
                             qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMInstance('CIM_Foo',
                             qualifiers={'Cheepy': 'birds'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Matching qualifiers, with values that is unicode / string",
        dict(
            obj1=CIMInstance('CIM_Foo',
                             qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMInstance('CIM_Foo',
                             qualifiers={'Cheepy': 'Birds'}),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Matching qualifiers, but with different order",
        dict(
            obj1=CIMInstance(
                'CIM_Foo',
                qualifiers=[
                    CIMQualifier('Q1', 'Birds'),
                    CIMQualifier('Q2', 'Ants'),
                ]
            ),
            obj2=CIMInstance(
                'CIM_Foo',
                qualifiers=[
                    CIMQualifier('Q2', 'Ants'),
                    CIMQualifier('Q1', 'Birds'),
                ]
            ),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Equal qualifiers with a number of types",
        dict(
            obj1=CIMInstance(
                'CIM_Foo',
                qualifiers={
                    'Name': 'Foo',
                    'Boolean': False,
                    'Number': Uint8(42),
                }
            ),
            obj2=CIMInstance(
                'CIM_Foo',
                qualifiers={
                    'Name': 'Foo',
                    'Boolean': False,
                    'Number': Uint8(42),
                }
            ),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Qualifier with different types: bool True / string 'TRUE'",
        dict(
            obj1=CIMInstance('CIM_Foo',
                             qualifiers={'Foo': True}),
            obj2=CIMInstance('CIM_Foo',
                             qualifiers={'Foo': 'TRUE'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Qualifier with different types: bool False / string 'FALSE'",
        dict(
            obj1=CIMInstance('CIM_Foo',
                             qualifiers={'Foo': False}),
            obj2=CIMInstance('CIM_Foo',
                             qualifiers={'Foo': 'FALSE'}),
            exp_equal=False,
        ),
        None, None, True
    ),
]

TESTCASES_CIMINSTANCE_EQ = [

    # Additional testcases for CIMInstance.__eq__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: CIMInstance object #1 to be tested.
    #   * obj2: CIMInstance object #2 to be tested.
    #   * exp_equal: Expected equality of the object hash values.
    # * 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

    # Exception testcases
    (
        "Invalid type of second object: string",
        dict(
            obj1=CIMInstance('CIM_Foo'),
            obj2='abc',
            exp_equal=None,
        ),
        TypeError, None, True
    ),
    (
        "Invalid type of second object: URI string",
        dict(
            obj1=CIMInstance('CIM_Foo'),
            obj2='http://abc:CIM_Foo',
            exp_equal=None,
        ),
        TypeError, None, True
    ),
    (
        "Invalid type of second object: CIMInstanceName",
        dict(
            obj1=CIMInstance('CIM_Foo'),
            obj2=CIMInstanceName('CIM_Foo'),
            exp_equal=None,
        ),
        TypeError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCE_HASH_EQ)
@simplified_test_function
@log_entry_exit
def test_CIMInstance_hash(testcase, obj1, obj2, exp_equal):
    """
    Test function for CIMInstance.__hash__().
    """

    # The code to be tested
    hash1 = hash(obj1)
    hash2 = hash(obj2)

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

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

    if exp_equal:
        assert hash1 == hash2
    else:
        assert hash1 != hash2


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCE_HASH_EQ + TESTCASES_CIMINSTANCE_EQ)
@simplified_test_function
@log_entry_exit
def test_CIMInstance_eq(testcase, obj1, obj2, exp_equal):
    """
    Test function for CIMInstance.__eq__().
    """

    # The code to be tested
    equal = (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

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

    assert equal == exp_equal


TESTCASES_CIMINSTANCE_STR_REPR = [

    # Testcases for CIMInstance.__repr__(), __str__() / repr(), str()

    # Each list item is a testcase tuple with these items:
    # * obj: CIMInstance object to be tested.

    (
        CIMInstance(
            classname='CIM_Foo')
    ),
    (
        CIMInstance(
            classname='CIM_Foo',
            properties=dict(
                Name='Spottyfoot',
                Ref1=CIMInstanceName('CIM_Bar')))
    ),
    (
        CIMInstance(
            classname='CIM_Foo',
            properties=dict(
                Name='Spottyfoot',
                Ref1=CIMInstanceName('CIM_Bar')),
            path=CIMInstanceName(
                'CIM_Foo',
                keybindings=dict(Name='Spottyfoot')))
    ),
]


@pytest.mark.parametrize(
    "obj",
    TESTCASES_CIMINSTANCE_STR_REPR)
@log_entry_exit
def test_CIMInstance_str(obj):
    """
    Test function for CIMInstance.__str__() / str()
    """

    # The code to be tested
    s = str(obj)

    assert re.match(r'^CIMInstance\(', s)

    exp_classname = _format('classname={0!A}', obj.classname)
    assert exp_classname in s

    exp_path = _format('path={0!A}', obj.path)
    assert exp_path in s


@pytest.mark.parametrize(
    "obj",
    TESTCASES_CIMINSTANCE_STR_REPR)
@log_entry_exit
def test_CIMInstance_repr(obj):
    """
    Test function for CIMInstance.__repr__() / repr()
    """

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

    assert re.match(r'^CIMInstance\(', r)

    exp_classname = _format('classname={0!A}', obj.classname)
    assert exp_classname in r

    exp_path = _format('path={0!A}', obj.path)
    assert exp_path in r

    exp_properties = _format('properties={0!A}', obj.properties)
    assert exp_properties in r

    exp_qualifiers = _format('qualifiers={0!A}', obj.qualifiers)
    assert exp_qualifiers in r


# Some CIMInstance objects for CIMInstance.tocimxml() tests

CIMINSTANCE_INV_PROPERTY_1 = CIMInstance('CIM_Foo')
CIMINSTANCE_INV_PROPERTY_1.properties['Foo'] = 'bla'  # no CIMProperty

CIMINSTANCE_INV_PROPERTY_2 = CIMInstance('CIM_Foo')
CIMINSTANCE_INV_PROPERTY_2.properties['Foo'] = Uint16(42)  # no CIMProperty

# Some CIMInstanceName objects for CIMInstance.tocimxml() tests

CIMINSTANCENAME_CLASSNAME1 = CIMInstanceName('CIM_Foo')
CIMINSTANCENAME_NAMESPACE1 = CIMInstanceName('CIM_Foo', namespace='interop')
CIMINSTANCENAME_HOST1 = CIMInstanceName('CIM_Foo', namespace='interop',
                                        host='woot.com')

TESTCASES_CIMINSTANCE_TOCIMXML = [

    # Testcases for CIMInstance.tocimxml() and tocimxmlstr()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: CIMInstance object to be tested.
    #   * kwargs: Dict of input args for tocimxml().
    #   * exp_xml_str: Expected CIM-XML string, as a tuple/list of parts.
    # * 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

    # Tests with path and args variations
    (
        "No path, implied default args",
        dict(
            obj=CIMInstance('CIM_Foo'),
            kwargs={},
            exp_xml_str=(
                '<INSTANCE CLASSNAME="CIM_Foo"/>',
            )
        ),
        None, None, True
    ),
    (
        "No path, specified default args",
        dict(
            obj=CIMInstance('CIM_Foo'),
            kwargs=dict(
                ignore_path=False,
            ),
            exp_xml_str=(
                '<INSTANCE CLASSNAME="CIM_Foo"/>',
            )
        ),
        None, None, True
    ),
    (
        "No path, non-default args ignore_path=True",
        dict(
            obj=CIMInstance('CIM_Foo'),
            kwargs=dict(
                ignore_path=True,
            ),
            exp_xml_str=(
                '<INSTANCE CLASSNAME="CIM_Foo"/>',
            )
        ),
        None, None, True
    ),
    (
        "Path with classname-only, implied default args",
        dict(
            obj=CIMInstance(
                'CIM_Foo',
                path=CIMINSTANCENAME_CLASSNAME1,
            ),
            kwargs={},
            exp_xml_str=(
                '<VALUE.NAMEDINSTANCE>',
                '<INSTANCENAME CLASSNAME="CIM_Foo"/>',
                '<INSTANCE CLASSNAME="CIM_Foo"/>',
                '</VALUE.NAMEDINSTANCE>',
            )
        ),
        None, MissingKeybindingsWarning, True
    ),
    (
        "Path with classname-only, specified default args",
        dict(
            obj=CIMInstance(
                'CIM_Foo',
                path=CIMINSTANCENAME_CLASSNAME1,
            ),
            kwargs=dict(
                ignore_path=False,
            ),
            exp_xml_str=(
                '<VALUE.NAMEDINSTANCE>',
                '<INSTANCENAME CLASSNAME="CIM_Foo"/>',
                '<INSTANCE CLASSNAME="CIM_Foo"/>',
                '</VALUE.NAMEDINSTANCE>',
            )
        ),
        None, MissingKeybindingsWarning, True
    ),
    (
        "Path with classname-only, non-default args ignore_path=True",
        dict(
            obj=CIMInstance(
                'CIM_Foo',
                path=CIMINSTANCENAME_CLASSNAME1,
            ),
            kwargs=dict(
                ignore_path=True,
            ),
            exp_xml_str=(
                '<INSTANCE CLASSNAME="CIM_Foo"/>',
            )
        ),
        None, None, True
    ),
    (
        "Path with namespace, implied default args",
        dict(
            obj=CIMInstance(
                'CIM_Foo',
                path=CIMINSTANCENAME_NAMESPACE1,
            ),
            kwargs={},
            exp_xml_str=(
                '<VALUE.OBJECTWITHLOCALPATH>',
                '<LOCALINSTANCEPATH>',
                '<LOCALNAMESPACEPATH>',
                '<NAMESPACE NAME="interop"/>',
                '</LOCALNAMESPACEPATH>',
                '<INSTANCENAME CLASSNAME="CIM_Foo"/>',
                '</LOCALINSTANCEPATH>',
                '<INSTANCE CLASSNAME="CIM_Foo"/>',
                '</VALUE.OBJECTWITHLOCALPATH>',
            )
        ),
        None, MissingKeybindingsWarning, True
    ),
    (
        "Path with namespace, specified default args",
        dict(
            obj=CIMInstance(
                'CIM_Foo',
                path=CIMINSTANCENAME_NAMESPACE1,
            ),
            kwargs=dict(
                ignore_path=False,
            ),
            exp_xml_str=(
                '<VALUE.OBJECTWITHLOCALPATH>',
                '<LOCALINSTANCEPATH>',
                '<LOCALNAMESPACEPATH>',
                '<NAMESPACE NAME="interop"/>',
                '</LOCALNAMESPACEPATH>',
                '<INSTANCENAME CLASSNAME="CIM_Foo"/>',
                '</LOCALINSTANCEPATH>',
                '<INSTANCE CLASSNAME="CIM_Foo"/>',
                '</VALUE.OBJECTWITHLOCALPATH>',
            )
        ),
        None, MissingKeybindingsWarning, True
    ),
    (
        "Path with namespace, non-default args ignore_path=True",
        dict(
            obj=CIMInstance(
                'CIM_Foo',
                path=CIMINSTANCENAME_NAMESPACE1,
            ),
            kwargs=dict(
                ignore_path=True,
            ),
            exp_xml_str=(
                '<INSTANCE CLASSNAME="CIM_Foo"/>',
            )
        ),
        None, None, True
    ),
    (
        "Path with namespace+host, implied default args",
        dict(
            obj=CIMInstance(
                'CIM_Foo',
                path=CIMINSTANCENAME_HOST1,
            ),
            kwargs={},
            exp_xml_str=(
                '<VALUE.INSTANCEWITHPATH>',
                '<INSTANCEPATH>',
                '<NAMESPACEPATH>',
                '<HOST>woot.com</HOST>',
                '<LOCALNAMESPACEPATH>',
                '<NAMESPACE NAME="interop"/>',
                '</LOCALNAMESPACEPATH>',
                '</NAMESPACEPATH>',
                '<INSTANCENAME CLASSNAME="CIM_Foo"/>',
                '</INSTANCEPATH>',
                '<INSTANCE CLASSNAME="CIM_Foo"/>',
                '</VALUE.INSTANCEWITHPATH>',
            )
        ),
        None, MissingKeybindingsWarning, True
    ),
    (
        "Path with namespace+host, specified default args",
        dict(
            obj=CIMInstance(
                'CIM_Foo',
                path=CIMINSTANCENAME_HOST1,
            ),
            kwargs=dict(
                ignore_path=False,
            ),
            exp_xml_str=(
                '<VALUE.INSTANCEWITHPATH>',
                '<INSTANCEPATH>',
                '<NAMESPACEPATH>',
                '<HOST>woot.com</HOST>',
                '<LOCALNAMESPACEPATH>',
                '<NAMESPACE NAME="interop"/>',
                '</LOCALNAMESPACEPATH>',
                '</NAMESPACEPATH>',
                '<INSTANCENAME CLASSNAME="CIM_Foo"/>',
                '</INSTANCEPATH>',
                '<INSTANCE CLASSNAME="CIM_Foo"/>',
                '</VALUE.INSTANCEWITHPATH>',
            )
        ),
        None, MissingKeybindingsWarning, True
    ),
    (
        "Path with namespace+host, non-default args ignore_path=True",
        dict(
            obj=CIMInstance(
                'CIM_Foo',
                path=CIMINSTANCENAME_HOST1,
            ),
            kwargs=dict(
                ignore_path=True,
            ),
            exp_xml_str=(
                '<INSTANCE CLASSNAME="CIM_Foo"/>',
            )
        ),
        None, None, True
    ),

    # Tests with property variations
    (
        "Two properties",
        dict(
            obj=CIMInstance(
                'CIM_Foo',
                properties=[
                    CIMProperty('P1', 'bla'),
                    CIMProperty('P2', 42, type='uint16'),
                ],
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCE CLASSNAME="CIM_Foo">',
                '<PROPERTY NAME="P1" TYPE="string">',
                '<VALUE>bla</VALUE>',
                '</PROPERTY>',
                '<PROPERTY NAME="P2" TYPE="uint16">',
                '<VALUE>42</VALUE>',
                '</PROPERTY>',
                '</INSTANCE>',
            )
        ),
        None, None, True
    ),

    # Tests with qualifier variations
    (
        "Two qualifiers",
        dict(
            obj=CIMInstance(
                'CIM_Foo',
                qualifiers=[
                    CIMQualifier('Q2', 'bla'),
                    CIMQualifier('Q1', True),
                ],
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCE CLASSNAME="CIM_Foo">',
                '<QUALIFIER NAME="Q2" TYPE="string">',
                '<VALUE>bla</VALUE>',
                '</QUALIFIER>',
                '<QUALIFIER NAME="Q1" TYPE="boolean">',
                '<VALUE>TRUE</VALUE>',
                '</QUALIFIER>',
                '</INSTANCE>',
            )
        ),
        None, None, True
    ),

    # Tests with qualifier, property variations
    (
        "Two qualifiers and two properties",
        dict(
            obj=CIMInstance(
                'CIM_Foo',
                qualifiers=[
                    CIMQualifier('Q2', 'bla'),
                    CIMQualifier('Q1', True),
                ],
                properties=[
                    CIMProperty('P2', 42, type='uint16'),
                    CIMProperty('P1', 'bla'),
                ],
            ),
            kwargs={},
            exp_xml_str=(
                '<INSTANCE CLASSNAME="CIM_Foo">',
                '<QUALIFIER NAME="Q2" TYPE="string">',
                '<VALUE>bla</VALUE>',
                '</QUALIFIER>',
                '<QUALIFIER NAME="Q1" TYPE="boolean">',
                '<VALUE>TRUE</VALUE>',
                '</QUALIFIER>',
                '<PROPERTY NAME="P2" TYPE="uint16">',
                '<VALUE>42</VALUE>',
                '</PROPERTY>',
                '<PROPERTY NAME="P1" TYPE="string">',
                '<VALUE>bla</VALUE>',
                '</PROPERTY>',
                '</INSTANCE>',
            )
        ),
        None, None, True
    ),

    # Tests with invalid property objects
    (
        "With invalid property object (type string)",
        dict(
            obj=CIMINSTANCE_INV_PROPERTY_1,
            kwargs={},
            exp_xml_str=None
        ),
        # raises TypeError since 0.12
        TypeError, None, True
    ),
    (
        "With invalid property object (type Uint16)",
        dict(
            obj=CIMINSTANCE_INV_PROPERTY_2,
            kwargs={},
            exp_xml_str=None
        ),
        # raises TypeError since 0.12
        TypeError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCE_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMInstance_tocimxml(testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMInstance.tocimxml().
    """

    # The code to be tested
    obj_xml = obj.tocimxml(**kwargs)

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

    obj_xml_str = obj_xml.toxml()
    exp_xml_str = ''.join(exp_xml_str)
    validate_cim_xml_obj(obj, obj_xml_str, exp_xml_str)


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCE_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMInstance_tocimxmlstr(testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMInstance.tocimxmlstr().
    """

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(**kwargs)

    # 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(obj_xml_str, str)

    exp_xml_str = ''.join(exp_xml_str)
    validate_cim_xml_obj(obj, obj_xml_str, exp_xml_str)


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCE_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMInstance_tocimxmlstr_indent_int(
        testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMInstance.tocimxmlstr() with indent as integer.
    """

    indent = 4
    indent_str = ' ' * indent

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(indent=indent, **kwargs)

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

    obj_xml = obj.tocimxml(**kwargs)  # This is tested elsewhere
    exp_xml_str = obj_xml.toprettyxml(indent=indent_str)

    assert obj_xml_str == exp_xml_str, (
        f"{obj.__class__.__name__}.tocimxmlstr(indent={indent!r}) "
        "returns unexpected CIM-XML string")


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCE_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMInstance_tocimxmlstr_indent_str(
        testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMInstance.tocimxmlstr() with indent as string.
    """

    indent = 4
    indent_str = ' ' * indent

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(indent=indent_str, **kwargs)

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

    obj_xml = obj.tocimxml(**kwargs)  # This is tested elsewhere
    exp_xml_str = obj_xml.toprettyxml(indent=indent_str)

    assert obj_xml_str == exp_xml_str, (
        f"{obj.__class__.__name__}.tocimxmlstr(indent={indent_str!r}) "
        "returns unexpected CIM-XML string")


TESTCASES_CIMINSTANCE_TOMOF = [

    # Testcases for CIMInstance.tomof()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: CIMInstance object to be tested.
    #   * kwargs: Dict of input args to tomof() method
    #   * exp_mof: Expected MOF result string, or None if failed.
    # * 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

    (
        "Instance without properties",
        dict(
            obj=CIMInstance(classname='C1'),
            kwargs={},
            exp_mof="""\
instance of C1 {
};
""",
        ),
        None, None, True
    ),
    (
        "all components",
        dict(
            obj=CIMInstance(
                classname='C1',
                properties=[
                    CIMProperty('p1', value=None, type='string'),
                ],
            ),
            kwargs={},
            exp_mof="""\
instance of C1 {
   p1 = NULL;
};
""",
        ),
        None, None, True
    ),
    (
        "Instance with NULL value on string property",
        dict(
            obj=CIMInstance(
                classname='C1',
                properties=[
                    CIMProperty('p1', value=None, type='string'),
                ],
            ),
            kwargs={},
            exp_mof="""\
instance of C1 {
   p1 = NULL;
};
""",
        ),
        None, None, True
    ),
    (
        "Instance with string property",
        dict(
            obj=CIMInstance(
                classname='C1',
                properties=[
                    CIMProperty('p1', value='abc'),
                ],
            ),
            kwargs={},
            exp_mof="""\
instance of C1 {
   p1 = "abc";
};
""",
        ),
        None, None, True
    ),
    (
        "Instance with uint8 property",
        dict(
            obj=CIMInstance(
                classname='C1',
                properties=[
                    CIMProperty('p1', value=Uint8(7)),
                ],
            ),
            kwargs={},
            exp_mof="""\
instance of C1 {
   p1 = 7;
};
""",
        ),
        None, None, True
    ),
    (
        "Instance with sint8 array property",
        dict(
            obj=CIMInstance(
                classname='C1',
                properties=[
                    CIMProperty('p1', value=[Sint8(-1), Sint8(5)]),
                ],
            ),
            kwargs={},
            exp_mof="""\
instance of C1 {
   p1 = { -1, 5 };
};
""",  # bug fixed in 0.12
        ),
        None, None, True
    ),
    (
        "Instance with embedded instance property having one key",
        dict(
            obj=CIMInstance(
                classname='C1',
                properties=[
                    CIMProperty(
                        'p1',
                        value=CIMInstance(
                            classname='EC',
                            properties=[
                                ('e1', 'abc'),
                            ],
                        ),
                        embedded_object='instance',
                    ),
                ],
            ),
            kwargs={},
            exp_mof="""\
instance of C1 {
   p1 = "instance of EC {\\n   e1 = \\"abc\\";\\n};\\n";
};
""",
        ),
        None, None, True
    ),
    (
        "Instance with embedded instance property having multiple keys",
        dict(
            obj=CIMInstance(
                classname='C1',
                properties=[
                    CIMProperty(
                        'p1',
                        value=CIMInstance(
                            classname='EC',
                            properties=[
                                ('e1', 'abc'),
                                ('e2', Uint32(42)),
                                ('e3',
                                 CIMDateTime('19980125133015.123456-300')),
                            ],
                        ),
                        embedded_object='instance',
                    ),
                ],
            ),
            kwargs={},
            exp_mof="""\
instance of C1 {
   p1 =
      "instance of EC {\\n   e1 = \\"abc\\";\\n   e2 = 42;\\n   e3 = "
      "\\"19980125133015.123456-300\\";\\n};\\n";
};
""",
        ),
        # Unpredictable order of keys before 0.12
        None, None, True
    ),
    (
        "Instance with embedded instance property as EmbeddedObject",
        dict(
            obj=CIMInstance(
                classname='C1',
                properties=[
                    CIMProperty(
                        'p1',
                        value=CIMInstance(
                            classname='EC',
                            properties=[
                                ('e1', 'abc'),
                            ],
                        ),
                        embedded_object='object',
                    ),
                ],
            ),
            kwargs={},
            exp_mof="""\
instance of C1 {
   p1 = "instance of EC {\\n   e1 = \\"abc\\";\\n};\\n";
};
""",
        ),
        None, None, True
    ),
    (
        "Instance with embedded class property as EmbeddedObject",
        dict(
            obj=CIMInstance(
                classname='C1',
                properties=[
                    CIMProperty(
                        'p1',
                        value=CIMClass(
                            classname='EC',
                            properties=[
                                CIMProperty(
                                    'e1',
                                    value=None,
                                    type='uint32',
                                ),
                            ],
                        ),
                        embedded_object='object',
                    ),
                ],
            ),
            kwargs={},
            exp_mof="""\
instance of C1 {
   p1 = "class EC {\\n\\n   uint32 e1;\\n\\n};\\n";
};
""",
        ),
        None, None, True
    ),
    (
        "Instance with reference property",
        dict(
            obj=CIMInstance(
                classname='C1',
                properties=[
                    CIMProperty(
                        'p1',
                        value=CIMInstanceName(
                            classname='RC',
                            keybindings=[
                                ('k1', "abc"),
                            ],
                        ),
                        reference_class='RC',
                    ),
                ],
            ),
            kwargs={},
            exp_mof="""\
instance of C1 {
   p1 = "/:RC.k1=\\"abc\\"";
};
""",  # bug fixed in 0.12
        ),
        None, None, True
    ),
    (
        "Instance with uint64 property value that does not fit onto line by 1",
        dict(
            obj=CIMInstance(
                classname='C1',
                properties=[
                    CIMProperty('p1', value=Uint64(1998012513304050)),
                ],
            ),
            kwargs=dict(
                maxline=22,
            ),
            exp_mof=None,
        ),
        ValueError, None, True
    ),
    (
        "Instance with uint64 property value that exactly fits onto line",
        dict(
            obj=CIMInstance(
                classname='C1',
                properties=[
                    CIMProperty('p1', value=Uint64(1998012513304050)),
                ],
            ),
            kwargs=dict(
                maxline=23,
            ),
            exp_mof=''
            'instance of C1 {\n'
            '   p1 =\n'
            '      1998012513304050;\n'
            '};\n',
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCE_TOMOF)
@simplified_test_function
@log_entry_exit
def test_CIMInstance_tomof(testcase, obj, kwargs, exp_mof):
    """
    Test function for CIMInstance.tomof().
    """

    # The code to be tested
    mof = obj.tomof(**kwargs)

    # 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(mof, str)
    assert mof == exp_mof


# Components to build the test class for the CIMInstance.from_class() test
TST_CLS = 'CIM_Foo'
ID_PROP = CIMProperty('ID', None, type='string', class_origin=TST_CLS,
                      qualifiers={'Key': CIMQualifier('Key', True)})
STR_PROP = CIMProperty('STR', None, type='string', class_origin=TST_CLS)
INT_PROP = CIMProperty('U32', None, type='uint32', class_origin=TST_CLS)
ARR_PROP = CIMProperty('A32', None, type='uint32', is_array=True,
                       class_origin=TST_CLS)
REF_PROP = CIMProperty('REF', None, type='reference', class_origin=TST_CLS,
                       reference_class='CIM_Foo')
# Properties with default in class
ID_PROP_D = CIMProperty('ID', "cls_id", type='string', class_origin=TST_CLS,
                        qualifiers={'Key': CIMQualifier('Key', True)})
STR_PROP_D = CIMProperty('STR', "cls_str", type='string',
                         class_origin=TST_CLS)
INT_PROP_D = CIMProperty('U32', 4, type='uint32', class_origin=TST_CLS)


# Temporary flags to clarify state of each test below.
# set condition to OK for tests that we know passed.  Setting OK to True runs
#     all tests and OK = False, bypasses all tests with OK
# Set condition to FAIL for any tests currently failing. Bypasses these tests
# Set condition to RUN for test currently being run
OK = True
FAIL = False
RUN = True

TESTCASES_CIMINSTANCE_FROMCLASS = [

    # Testcases for CIMInstance.from_class()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * cls_props: Dict of input properties for CIMClass.
    #   * inst_prop_vals: Dict of property values for the instance.
    #     Order required in this dict so limited to py>=3.7.
    #   * kwargs: Dict of input args to from_class method
    #   * exp_props: Expected properties in created instance as a dictionary
    # * 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

    # Tests based on class with 3 properties
    (
        "Verify same properties in instance and class and default params"
        " passes (returns instance that matches exp_props",
        dict(
            cls_props=(ID_PROP, STR_PROP, INT_PROP),
            inst_prop_vals=dict(
                [('ID', 'inst_id'), ('STR', 'str_val'),
                 ('U32', Uint32(3))]),
            kwargs={},
            exp_props={'ID': 'inst_id', 'STR': 'str_val',
                       'U32': Uint32(3)},
        ),
        None, None, OK
    ),
    (
        "Verify same property names case independent. Instance properties set"
        "to lower case in test",
        dict(
            cls_props=(ID_PROP, STR_PROP, INT_PROP),
            inst_prop_vals=dict(
                [('id', 'inst_id'), ('str', 'str_val'),
                 ('u32', Uint32(3))]),
            kwargs={'include_path': True},
            exp_props={'ID': 'inst_id', 'STR': 'str_val',
                       'U32': Uint32(3)},
        ),
        None, None, OK
    ),
    (
        "Verify same property names case independent. This only works if "
        "property_values is a case independent dictionary.",
        dict(
            cls_props=(ID_PROP, STR_PROP, INT_PROP),
            inst_prop_vals=dict(
                [('id', 'inst_id'), ('str', 'str_val'),
                 ('u32', Uint32(3))]),
            kwargs={'include_path': True},
            exp_props={'id': 'inst_id', 'str': 'str_val',
                       'u32': Uint32(3)},
        ),
        None, None, OK
    ),
    (
        "Verify same properties in instance and class and default params"
        " passes (returns instance that matches exp_props",
        dict(
            cls_props=(ID_PROP, STR_PROP, INT_PROP, REF_PROP),
            inst_prop_vals=dict(
                [('ID', 'inst_id'), ('STR', 'str_val'),
                 ('U32', Uint32(3)),
                 ('REF', CIMInstanceName('CIM_Foo',
                                         keybindings={'InstID': '1234'},
                                         host='woot.com',
                                         namespace='root/cimv2'))]),
            kwargs={},
            exp_props={'ID': 'inst_id', 'STR': 'str_val',
                       'U32': Uint32(3),
                       'REF': CIMProperty('REF',
                                          CIMInstanceName(
                                              'CIM_Foo',
                                              keybindings={'InstID': '1234'},
                                              host='woot.com',
                                              namespace='root/cimv2'),
                                          type='reference',
                                          reference_class='CIM_Foo')},
        ),
        None, None, OK
    ),
    (
        "Verify class with only 2 props defined in instance, 3 in class. "
        "inc_null_prop=True passes",
        dict(
            cls_props=(ID_PROP, STR_PROP, INT_PROP),
            inst_prop_vals=dict(
                [('ID', 'inst_id'), ('STR', 'str_val')]),
            kwargs={'include_missing_properties': True},
            exp_props={'ID': 'inst_id', 'STR': 'str_val',
                       'U32': CIMProperty('U32', None, type='uint32')},
        ),
        None, None, OK
    ),
    (
        "Verify 2 props defined in instance inc_nul_prop=True passes",
        dict(
            cls_props=(ID_PROP, STR_PROP, INT_PROP),
            inst_prop_vals=dict(
                [('ID', 'inst_id'), ('STR', 'str_val')]),
            kwargs={'include_missing_properties': True},
            exp_props={'ID': 'inst_id', 'STR': 'str_val',
                       'U32': CIMProperty('U32', None, type='uint32')},
        ),
        None, None, OK
    ),
    (
        "Verify 2 props defined in instance inc_null_prop=False passes",
        dict(
            cls_props=(ID_PROP, STR_PROP, INT_PROP),
            inst_prop_vals=dict(
                [('ID', 'inst_id'), ('STR', 'str_val')]),
            kwargs={'include_missing_properties': False},
            exp_props={'ID': 'inst_id', 'STR': 'str_val'},
        ),
        None, None, OK
    ),

    # Test include path option
    (
        "Verify class and inst with 3 properties tests include path = True "
        "passes.",
        dict(
            cls_props=(ID_PROP, STR_PROP, INT_PROP),
            inst_prop_vals=dict(
                [('ID', 'inst_id'), ('STR', 'str_val'),
                 ('U32', Uint32(3))]),
            kwargs={'include_path': True},
            exp_props={'ID': 'inst_id', 'STR': 'str_val',
                       'U32': Uint32(3)},
        ),
        None, None, OK
    ),
    (
        "Verify Class with 3 properties, params: include_path=False passes",
        dict(
            cls_props=(ID_PROP, STR_PROP, INT_PROP),
            inst_prop_vals=dict(
                [('ID', 'inst_id'), ('STR', 'str_val'),
                 ('U32', Uint32(3))]),
            kwargs={'include_path': False},
            exp_props={'ID': 'inst_id', 'STR': 'str_val',
                       'U32': Uint32(3)},
        ),
        None, None, OK
    ),

    # test expect properties value None
    (
        "Verify instance with same props as class and one prop with None "
        "passes",
        dict(
            cls_props=(ID_PROP, STR_PROP, INT_PROP),
            inst_prop_vals=dict(
                [('ID', 'inst_id'), ('STR', 'blah'), ('U32', None)]),
            kwargs={'include_path': False},
            exp_props={'ID': 'inst_id', 'STR': "blah",
                       'U32': CIMProperty('U32', None, 'uint32')},
        ),
        None, None, OK
    ),

    # test include_class_origin = True
    (
        "Verify class with one property. include_class_origin=True passes",
        dict(
            cls_props=(ID_PROP, STR_PROP, INT_PROP),
            inst_prop_vals=dict([('ID', 'inst_id')]),
            kwargs={'include_path': False, 'include_class_origin': True,
                    'include_missing_properties': False},
            exp_props={'ID': CIMProperty('ID', 'inst_id', type='string',
                                         class_origin='CIM_Foo')},
        ),
        None, None, OK
    ),
    (
        "Verify same properties in instance & class inc_path=True, "
        "creates new instance with path",
        dict(
            cls_props=(ID_PROP, STR_PROP, INT_PROP),
            inst_prop_vals=dict(
                [('ID', 'inst_id'), ('STR', 'str_val'),
                 ('U32', Uint32(3))]),
            kwargs={'include_path': True},
            exp_props={'ID': 'inst_id', 'STR': 'str_val',
                       'U32': Uint32(3)},
        ),
        None, None, OK
    ),
    (
        "Verify instance with missing id property in instance flags: inc path, "
        "include_missing_properties=False fails.",
        dict(
            cls_props=(ID_PROP, STR_PROP, INT_PROP),
            inst_prop_vals=dict(
                [('STR', 'str_val'), ('U32', Uint32(99999))]),
            kwargs={'include_path': True,
                    'include_missing_properties': False},
            exp_props={'ID': 'inst_id', 'STR': 'str_val',
                       'U32': Uint32(99999)},
        ),
        ValueError, None, OK
    ),
    (
        "Instance with missing id property in instance flags: inc path, "
        "test fails, key propety value None",
        dict(
            cls_props=(ID_PROP, STR_PROP, INT_PROP),
            inst_prop_vals=dict(
                [('STR', 'str_val'), ('U32', Uint32(99999))]),
            kwargs={'include_path': True},
            exp_props={'ID': 'inst_id', 'STR': 'str_val',
                       'U32': Uint32(99999)},
        ),
        ValueError, None, OK
    ),
    (
        "Instance with property in instance but not in class fails",
        dict(
            cls_props=(ID_PROP, STR_PROP, INT_PROP),
            inst_prop_vals=dict(
                [('ID', 'inst_id'), ('STR', 'str_val'), ('U32', Uint32(3)),
                 ('BLAH', Uint64(9))]),
            kwargs={},
            exp_props={'ID': 'inst_id', 'STR': 'str_val',
                       'U32': Uint32(3)},
        ),
        ValueError, None, OK
    ),
    (
        "Instance with property type different than class fails",
        dict(
            cls_props=(ID_PROP, STR_PROP, INT_PROP),
            inst_prop_vals=dict(
                [('ID', 'inst_id'), ('STR', 'str_val'), ('U32', 'blah')]),
            kwargs={},
            exp_props={'ID': 'inst_id', 'STR': 'str_val',
                       'U32': Uint32(3)},
        ),
        ValueError, None, OK
    ),
    (
        "Class with with valid array property passes test",
        dict(
            cls_props=(ID_PROP, STR_PROP, INT_PROP, ARR_PROP),
            inst_prop_vals=dict(
                [('ID', 'inst_id'), ('STR', 'str_val'), ('U32', Uint32(3)),
                 ('A32', [Uint32(3), Uint32(9999)])]),
            kwargs={},
            exp_props={'ID': 'inst_id', 'STR': 'str_val',
                       'U32': Uint32(3), 'A32': [Uint32(3), Uint32(9999)]},
        ),
        None, None, OK
    ),
    (
        "Class with with array property but non-array in instance fails",
        dict(
            cls_props=(ID_PROP, STR_PROP, INT_PROP, ARR_PROP),
            inst_prop_vals=dict(
                [('ID', 'inst_id'), ('STR', 'str_val'), ('U32', Uint32(3)),
                 ('A32', Uint32(3))]),
            kwargs={},
            exp_props={'ID': 'inst_id', 'STR': 'str_val',
                       'U32': Uint32(3), 'A32': Uint32(3)},
        ),
        ValueError, None, OK
    ),
    (
        "Same properties in instance and class. Default params with namespace",
        dict(
            cls_props=(ID_PROP, STR_PROP, INT_PROP),
            inst_prop_vals=dict(
                [('ID', 'inst_id'), ('STR', 'str_val'),
                 ('U32', Uint32(3))]),
            kwargs={'namespace': 'root/blah', 'include_path': True},
            exp_props={'ID': 'inst_id', 'STR': 'str_val',
                       'U32': Uint32(3)},
        ),
        None, None, OK
    ),

    # Tests with defaults prop values in class
    (
        "Class with default props. No props in instance. Fails because "
        "include_missing_properties not set. No properties set in instance",
        dict(
            cls_props=(ID_PROP_D, STR_PROP_D, INT_PROP_D),
            inst_prop_vals=dict(),
            kwargs={'include_path': True,
                    'include_missing_properties': False},
            exp_props={'ID': 'cls_id', 'STR': 'cls_str', 'U32': Uint32(4)},
        ),
        ValueError, None, OK
    ),
    (
        "Class with default props. No props in instance passes because "
        "include_missing_properties is set",
        dict(
            cls_props=(ID_PROP_D, STR_PROP_D, INT_PROP_D),
            inst_prop_vals=dict(),
            kwargs={'include_path': True,
                    'include_missing_properties': True},
            exp_props={'ID': 'cls_id', 'STR': 'cls_str', 'U32': Uint32(4)},
        ),
        None, None, OK
    ),
    (
        "Class default props. All props in instance. ",
        dict(
            cls_props=(ID_PROP_D, STR_PROP_D, INT_PROP_D),
            inst_prop_vals=dict(
                [('ID', 'inst_id'), ('STR', 'str_val'), ('U32', Uint32(3)),
                 ('A32', Uint32(3))]),
            kwargs={'include_path': True,
                    'include_missing_properties': False},
            exp_props={'ID': 'inst_id', 'STR': 'str_val',
                       'U32': Uint32(3)},
        ),
        ValueError, None, OK
    ),
    (
        "Verify no input properties via empty dict",
        dict(
            cls_props=(ID_PROP_D, STR_PROP_D, INT_PROP_D),
            inst_prop_vals=dict(),
            kwargs={'include_path': False,
                    'include_missing_properties': False},
            exp_props={},
        ),
        None, None, OK
    ),
    (
        "Verify no input properties via None",
        dict(
            cls_props=(ID_PROP_D, STR_PROP_D, INT_PROP_D),
            inst_prop_vals=None,
            kwargs={'include_path': False,
                    'include_missing_properties': False},
            exp_props={},
        ),
        None, None, OK
    ),
    (
        "Verify invalid type for property_values causes exception",
        dict(
            cls_props=(ID_PROP_D, STR_PROP_D, INT_PROP_D),
            inst_prop_vals='blah',
            kwargs={'include_path': False,
                    'include_missing_properties': False},
            exp_props=None,
        ),
        TypeError, None, OK
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMINSTANCE_FROMCLASS)
@simplified_test_function
@log_entry_exit
def test_CIMInstance_from_class(testcase, cls_props, inst_prop_vals, kwargs,
                                exp_props):
    """
    Test the static method CIMInstance.from_class() where cls_props defines the
    class properties used to build the class, inst_prop_vals defines the
    property values to be input into the property_values parameter of the
    method being tested, kwargs defines any other arguments for the method
    call, and exp_props defines the properties expected in the created
    instance.
    """
    # Create the test class
    cim_class = CIMClass('CIM_Foo', properties=cls_props)

    # define expected params for call with their defaults for result tests
    include_path = kwargs.get('include_path', True)
    namespace = kwargs.get('namespace', None)
    include_class_origin = kwargs.get('include_class_origin', False)

    # The code to be tested
    act_inst = CIMInstance.from_class(cim_class,
                                      property_values=inst_prop_vals,
                                      **kwargs)

    # 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(exp_props, dict)
    if not isinstance(exp_props, NocaseDict):
        exp_props = NocaseDict(exp_props.items())

    exp_inst = CIMInstance('CIM_Foo', properties=exp_props)

    if include_path:
        exp_inst.path = CIMInstanceName.from_instance(cim_class,
                                                      exp_inst,
                                                      namespace)
        assert act_inst.path
        assert act_inst.path.namespace == namespace
    else:
        assert act_inst.path is None

    for prop in act_inst.properties.values():
        assert isinstance(prop, CIMProperty)
        class_origin = prop.class_origin  # pylint: disable=no-member
        if include_class_origin:
            assert class_origin is not None
        else:
            assert class_origin is None

    assert exp_inst == act_inst


TESTCASES_CIMPROPERTY_INIT = [

    # Testcases for CIMProperty.__init__()

    # 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 CIMProperty().
    #   * init_kwargs: Dict of keyword arguments to CIMProperty().
    #   * exp_attrs: Dict of expected attributes of resulting 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

    # Order of positional arguments
    (
        "Verify order of positional arguments",
        dict(
            init_args=[
                'Prop1',
                [CIMInstance('CIM_Foo')],
                'string',
                'CIM_FooParent',
                5,
                True,
                True,
                None,
                NocaseDict([
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                ]),
                'instance',
            ],
            init_kwargs={},
            exp_attrs=dict(
                name='Prop1',
                value=[CIMInstance('CIM_Foo')],
                type='string',
                class_origin='CIM_FooParent',
                array_size=5,
                propagated=True,
                is_array=True,
                reference_class=None,
                qualifiers=NocaseDict([
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                ]),
                embedded_object='instance',
            )
        ),
        None, None, True
    ),

    # Name tests
    (
        "Verify that bytes name is converted to unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                name=b'FooProp', value='abc',
            ),
            exp_attrs=dict(
                name='FooProp', value='abc', type=exp_type_string,
            )
        ),
        None, None, True
    ),
    (
        "Verify that unicode name remains unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value='abc',
            ),
            exp_attrs=dict(
                name='FooProp', value='abc', type=exp_type_string,
            )
        ),
        None, None, True
    ),

    # Value/type tests: Initialization to CIM string type
    (
        "Verify bytes string without type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=b'abc',
            ),
            exp_attrs=dict(
                name='FooProp', value='abc', type=exp_type_string,
            )
        ),
        None, None, True
    ),
    (
        "Verify unicode string without type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value='abc',
            ),
            exp_attrs=dict(
                name='FooProp', value='abc', type=exp_type_string,
            )
        ),
        None, None, True
    ),
    (
        "Verify bytes string with type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=b'abc', type='string',
            ),
            exp_attrs=dict(
                name='FooProp', value='abc', type='string',
            )
        ),
        None, None, True
    ),
    (
        "Verify unicode string with type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value='abc', type='string',
            ),
            exp_attrs=dict(
                name='FooProp', value='abc', type='string',
            )
        ),
        None, None, True
    ),
    (
        "Verify None with type string",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=None, type='string',
            ),
            exp_attrs=dict(
                name='FooProp', value=None, type='string',
            )
        ),
        None, None, True
    ),

    # Value/type tests: Initialization to CIM integer types
    (
        "Verify Uint8 value without type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=Uint8(32),
            ),
            exp_attrs=dict(
                name='FooProp', value=Uint8(32), type=exp_type_uint8,
            )
        ),
        None, None, True
    ),
    (
        "Verify Uint8 value with type uint8",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=Uint8(32), type='uint8',
            ),
            exp_attrs=dict(
                name='FooProp', value=Uint8(32), type='uint8',
            )
        ),
        None, None, True
    ),
    (
        "Verify None value with type uint8",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=None, type='uint8',
            ),
            exp_attrs=dict(
                name='FooProp', value=None, type='uint8',
            )
        ),
        None, None, True
    ),
    (
        "Verify Uint16 value without type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=Uint16(32),
            ),
            exp_attrs=dict(
                name='FooProp', value=Uint16(32), type=exp_type_uint16,
            )
        ),
        None, None, True
    ),
    (
        "Verify Uint16 value with type uint16",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=Uint16(32), type='uint16',
            ),
            exp_attrs=dict(
                name='FooProp', value=Uint16(32), type='uint16',
            )
        ),
        None, None, True
    ),
    (
        "Verify None value with type uint16",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=None, type='uint16',
            ),
            exp_attrs=dict(
                name='FooProp', value=None, type='uint16',
            )
        ),
        None, None, True
    ),
    (
        "Verify Uint32 value without type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=Uint32(32),
            ),
            exp_attrs=dict(
                name='FooProp', value=Uint32(32), type=exp_type_uint32,
            )
        ),
        None, None, True
    ),
    (
        "Verify Uint32 value with type uint32",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=Uint32(32), type='uint32',
            ),
            exp_attrs=dict(
                name='FooProp', value=Uint32(32), type='uint32',
            )
        ),
        None, None, True
    ),
    (
        "Verify None value with type uint32",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=None, type='uint32',
            ),
            exp_attrs=dict(
                name='FooProp', value=None, type='uint32',
            )
        ),
        None, None, True
    ),
    (
        "Verify Uint64 value without type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=Uint64(32),
            ),
            exp_attrs=dict(
                name='FooProp', value=Uint64(32), type=exp_type_uint64,
            )
        ),
        None, None, True
    ),
    (
        "Verify Uint64 value with type uint64",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=Uint64(32), type='uint64',
            ),
            exp_attrs=dict(
                name='FooProp', value=Uint64(32), type='uint64',
            )
        ),
        None, None, True
    ),
    (
        "Verify None value with type uint64",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=None, type='uint64',
            ),
            exp_attrs=dict(
                name='FooProp', value=None, type='uint64',
            )
        ),
        None, None, True
    ),
    (
        "Verify Sint8 value without type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=Sint8(32),
            ),
            exp_attrs=dict(
                name='FooProp', value=Sint8(32), type=exp_type_sint8,
            )
        ),
        None, None, True
    ),
    (
        "Verify Sint8 value with type sint8",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=Sint8(32), type='sint8',
            ),
            exp_attrs=dict(
                name='FooProp', value=Sint8(32), type='sint8',
            )
        ),
        None, None, True
    ),
    (
        "Verify None value with type sint8",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=None, type='sint8',
            ),
            exp_attrs=dict(
                name='FooProp', value=None, type='sint8',
            )
        ),
        None, None, True
    ),
    (
        "Verify Sint16 value without type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=Sint16(32),
            ),
            exp_attrs=dict(
                name='FooProp', value=Sint16(32), type=exp_type_sint16,
            )
        ),
        None, None, True
    ),
    (
        "Verify Sint16 value with type sint16",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=Sint16(32), type='sint16',
            ),
            exp_attrs=dict(
                name='FooProp', value=Sint16(32), type='sint16',
            )
        ),
        None, None, True
    ),
    (
        "Verify None value with type sint16",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=None, type='sint16',
            ),
            exp_attrs=dict(
                name='FooProp', value=None, type='sint16',
            )
        ),
        None, None, True
    ),
    (
        "Verify Sint32 value without type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=Sint32(32),
            ),
            exp_attrs=dict(
                name='FooProp', value=Sint32(32), type=exp_type_sint32,
            )
        ),
        None, None, True
    ),
    (
        "Verify Sint32 value with type sint32",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=Sint32(32), type='sint32',
            ),
            exp_attrs=dict(
                name='FooProp', value=Sint32(32), type='sint32',
            )
        ),
        None, None, True
    ),
    (
        "Verify None value with type sint32",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=None, type='sint32',
            ),
            exp_attrs=dict(
                name='FooProp', value=None, type='sint32',
            )
        ),
        None, None, True
    ),
    (
        "Verify Sint64 value without type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=Sint64(32),
            ),
            exp_attrs=dict(
                name='FooProp', value=Sint64(32), type=exp_type_sint64,
            )
        ),
        None, None, True
    ),
    (
        "Verify Sint64 value with type sint64",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=Sint64(32), type='sint64',
            ),
            exp_attrs=dict(
                name='FooProp', value=Sint64(32), type='sint64',
            )
        ),
        None, None, True
    ),
    (
        "Verify None value with type sint64",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=None, type='sint64',
            ),
            exp_attrs=dict(
                name='FooProp', value=None, type='sint64',
            )
        ),
        None, None, True
    ),

    # Value/type tests: Initialization to CIM float types
    (
        "Verify Real32 value without type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=Real32(32.0),
            ),
            exp_attrs=dict(
                name='FooProp', value=Real32(32.0), type=exp_type_real32,
            )
        ),
        None, None, True
    ),
    (
        "Verify Real32 value with type real32",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=Real32(32.0), type='real32',
            ),
            exp_attrs=dict(
                name='FooProp', value=Real32(32.0), type='real32',
            )
        ),
        None, None, True
    ),
    (
        "Verify None value with type real32",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=None, type='real32',
            ),
            exp_attrs=dict(
                name='FooProp', value=None, type='real32',
            )
        ),
        None, None, True
    ),
    (
        "Verify Real64 value without type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=Real64(32.0),
            ),
            exp_attrs=dict(
                name='FooProp', value=Real64(32.0), type=exp_type_real64,
            )
        ),
        None, None, True
    ),
    (
        "Verify Real64 value with type real64",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=Real64(32.0), type='real64',
            ),
            exp_attrs=dict(
                name='FooProp', value=Real64(32.0), type='real64',
            )
        ),
        None, None, True
    ),
    (
        "Verify float value without type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=32.0,
            ),
            exp_attrs=None
        ),
        ValueError, None, True  # cannot infer type from value
    ),
    (
        "Verify float value with type real64",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=32.0, type='real64',
            ),
            exp_attrs=dict(
                name='FooProp', value=Real64(32.0), type='real64',
            )
        ),
        None, None, True
    ),
    (
        "Verify float value with type real32",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=32.0, type='real32',
            ),
            exp_attrs=dict(
                name='FooProp', value=Real32(32.0), type='real32',
            )
        ),
        None, None, True
    ),
    (
        "Verify None value with type real64",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=None, type='real64',
            ),
            exp_attrs=dict(
                name='FooProp', value=None, type='real64',
            )
        ),
        None, None, True
    ),

    # Value/type tests: Initialization to CIM boolean type
    (
        "Verify bool value True without type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=True,
            ),
            exp_attrs=dict(
                name='FooProp', value=True, type=exp_type_boolean,
            )
        ),
        None, None, True
    ),
    (
        "Verify bool value True with type boolean",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=True, type='boolean',
            ),
            exp_attrs=dict(
                name='FooProp', value=True, type='boolean',
            )
        ),
        None, None, True
    ),
    (
        "Verify bool value False without type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=False,
            ),
            exp_attrs=dict(
                name='FooProp', value=False, type=exp_type_boolean,
            )
        ),
        None, None, True
    ),
    (
        "Verify bool value False with type boolean",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=False, type='boolean',
            ),
            exp_attrs=dict(
                name='FooProp', value=False, type='boolean',
            )
        ),
        None, None, True
    ),
    (
        "Verify None value with type boolean",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=None, type='boolean',
            ),
            exp_attrs=dict(
                name='FooProp', value=None, type='boolean',
            )
        ),
        None, None, True
    ),

    # Value/type tests: Initialization to CIM datetime type
    (
        "Verify timedelta interval value without type (since 0.8.1)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=TIMEDELTA1_TD,
            ),
            exp_attrs=dict(
                name='FooProp', value=TIMEDELTA1_OBJ, type=exp_type_datetime,
            )
        ),
        None, None, True
    ),
    (
        "Verify timedelta interval value with type (since 0.8.1)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=TIMEDELTA1_TD, type='datetime',
            ),
            exp_attrs=dict(
                name='FooProp', value=TIMEDELTA1_OBJ, type='datetime',
            )
        ),
        None, None, True
    ),
    (
        "Verify CIMDateTime interval value without type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=CIMDateTime(TIMEDELTA1_TD),
            ),
            exp_attrs=dict(
                name='FooProp', value=TIMEDELTA1_OBJ, type=exp_type_datetime,
            )
        ),
        None, None, True
    ),
    (
        "Verify CIMDateTime interval value with type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=CIMDateTime(TIMEDELTA1_TD),
                type='datetime',
            ),
            exp_attrs=dict(
                name='FooProp', value=TIMEDELTA1_OBJ, type='datetime',
            )
        ),
        None, None, True
    ),
    (
        "Verify CIM datetime string interval value with type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=TIMEDELTA1_STR, type='datetime',
            ),
            exp_attrs=dict(
                name='FooProp', value=TIMEDELTA1_OBJ, type='datetime',
            )
        ),
        None, None, True
    ),
    (
        "Verify datetime point in time value without type (since 0.8.1)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=DATETIME1_DT,
            ),
            exp_attrs=dict(
                name='FooProp', value=DATETIME1_OBJ, type=exp_type_datetime,
            )
        ),
        None, None, True
    ),
    (
        "Verify datetime point in time value with type (since 0.8.1)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=DATETIME1_DT, type='datetime',
            ),
            exp_attrs=dict(
                name='FooProp', value=DATETIME1_OBJ, type='datetime',
            )
        ),
        None, None, True
    ),
    (
        "Verify CIMDateTime point in time value without type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=CIMDateTime(DATETIME1_DT),
            ),
            exp_attrs=dict(
                name='FooProp', value=DATETIME1_OBJ, type=exp_type_datetime,
            )
        ),
        None, None, True
    ),
    (
        "Verify CIMDateTime point in time value with type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=CIMDateTime(DATETIME1_DT),
                type='datetime',
            ),
            exp_attrs=dict(
                name='FooProp', value=DATETIME1_OBJ, type='datetime',
            )
        ),
        None, None, True
    ),
    (
        "Verify CIM datetime point in time string value with type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=DATETIME1_STR, type='datetime',
            ),
            exp_attrs=dict(
                name='FooProp', value=DATETIME1_OBJ, type='datetime',
            )
        ),
        None, None, True
    ),

    # Value/type/reference_class tests: Initialization to CIM ref type
    (
        "Verify that reference_class None is permitted for reference type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=None, type='reference',
                reference_class=None,
            ),
            exp_attrs=dict(
                name='FooProp', value=None, type='reference',
                reference_class=None,
            )
        ),
        None, None, True
    ),
    (
        "Verify that bytes reference_class is converted to unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=None, type='reference',
                reference_class=b'CIM_Ref',
            ),
            exp_attrs=dict(
                name='FooProp', value=None, type='reference',
                reference_class='CIM_Ref',
            )
        ),
        None, None, True
    ),
    (
        "Verify that unicode reference_class remains unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=None, type='reference',
                reference_class='CIM_Ref',
            ),
            exp_attrs=dict(
                name='FooProp', value=None, type='reference',
                reference_class='CIM_Ref',
            )
        ),
        None, None, True
    ),
    (
        "Verify that reference type is implied from CIMInstanceName value "
        "(since 0.8.1)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp',
                value=CIMINSTANCENAME_C1_OBJ,
            ),
            exp_attrs=dict(
                name='FooProp',
                value=CIMINSTANCENAME_C1_OBJ,
                type=exp_type_reference,
            )
        ),
        None, None, True
    ),
    (
        "Verify normal case with value, type, reference_class specified",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp',
                value=CIMINSTANCENAME_C1_OBJ,
                type='reference',
                reference_class=CIMINSTANCENAME_C1_OBJ.classname,
            ),
            exp_attrs=dict(
                name='FooProp',
                value=CIMINSTANCENAME_C1_OBJ,
                type='reference',
                reference_class=CIMINSTANCENAME_C1_OBJ.classname,
            )
        ),
        None, None, True
    ),
    (
        # This is just a rough test whether a WBEM URI string will be
        # converted into a CIMInstanceName object. A more thorough test
        # with variations of WBEM URI strings is in the test cases for
        # CIMInstanceName.from_wbem_uri().
        "Verify that WBEM URI string value is converted to CIMInstanceName",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp',
                value='https://10.1.2.3:5989/root/cimv2:C1.k1="v1",k2=True',
                type='reference',
            ),
            exp_attrs=dict(
                name='FooProp',
                value=CIMInstanceName(
                    'C1', keybindings=dict(k1='v1', k2=True),
                    namespace='root/cimv2', host='10.1.2.3:5989'),
                type='reference',
            )
        ),
        None, None, True
    ),

    # Value/type/embedded_object tests: Initialization to CIM embedded
    # object (instance or class)
    (
        "Verify that value None is permitted with embedded_object",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=None, type='string',
                embedded_object='object',
            ),
            exp_attrs=dict(
                name='FooProp', value=None, type='string',
                embedded_object='object',
            )
        ),
        None, None, True
    ),
    (
        "Verify that value CIMClass implies type and embedded_object",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=CIMCLASS_C1_OBJ,
            ),
            exp_attrs=dict(
                name='FooProp', value=CIMCLASS_C1_OBJ, type=exp_type_string,
                embedded_object=exp_eo_object,
            )
        ),
        None, None, True
    ),
    (
        "Verify that value CIMClass and type string imply embedded_object "
        "(since 0.8.1)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=CIMCLASS_C1_OBJ, type='string',
            ),
            exp_attrs=dict(
                name='FooProp', value=CIMCLASS_C1_OBJ, type='string',
                embedded_object=exp_eo_object,
            )
        ),
        None, None, True
    ),
    (
        "Verify that value CIMClass and embedded_object imply type string",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=CIMCLASS_C1_OBJ,
                embedded_object='object',
            ),
            exp_attrs=dict(
                name='FooProp', value=CIMCLASS_C1_OBJ, type=exp_type_string,
                embedded_object='object',
            )
        ),
        None, None, True
    ),
    (
        "Verify normal case with value, type, embedded_object specified",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=CIMCLASS_C1_OBJ, type='string',
                embedded_object='object',
            ),
            exp_attrs=dict(
                name='FooProp', value=CIMCLASS_C1_OBJ, type='string',
                embedded_object='object',
            )
        ),
        None, None, True
    ),
    (
        "Verify that value CIMInstance and embedded_object imply type "
        "string (since 0.8.1)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=CIMINSTANCE_C1_OBJ,
                embedded_object='object',
            ),
            exp_attrs=dict(
                name='FooProp', value=CIMINSTANCE_C1_OBJ, type=exp_type_string,
                embedded_object='object',
            )
        ),
        None, None, True
    ),
    (
        "Verify normal case with value CIMInstance, type, embedded_object "
        "specified",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=CIMINSTANCE_C1_OBJ, type='string',
                embedded_object='object',
            ),
            exp_attrs=dict(
                name='FooProp', value=CIMINSTANCE_C1_OBJ, type='string',
                embedded_object='object',
            )
        ),
        None, None, True
    ),

    # Value/type/embedded_object tests: Initialization to CIM embedded
    # instance
    (
        "Verify that value CIMInstance implies type and embedded_object",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=CIMINSTANCE_C1_OBJ,
            ),
            exp_attrs=dict(
                name='FooProp', value=CIMINSTANCE_C1_OBJ, type=exp_type_string,
                embedded_object=exp_eo_instance,
            )
        ),
        None, None, True
    ),
    (
        "Verify that value CIMInstance and type string imply "
        "embedded_object (since 0.8.1)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=CIMINSTANCE_C1_OBJ, type='string',
            ),
            exp_attrs=dict(
                name='FooProp', value=CIMINSTANCE_C1_OBJ, type='string',
                embedded_object=exp_eo_instance,
            )
        ),
        None, None, True
    ),
    (
        "Verify that value CIMInstance and embedded_object imply type "
        "string",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=CIMINSTANCE_C1_OBJ,
                embedded_object='instance',
            ),
            exp_attrs=dict(
                name='FooProp', value=CIMINSTANCE_C1_OBJ, type=exp_type_string,
                embedded_object='instance',
            )
        ),
        None, None, True
    ),
    (
        "Verify normal case with value CIMInstance, type, embedded_object "
        "specified",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=CIMINSTANCE_C1_OBJ, type='string',
                embedded_object='instance',
            ),
            exp_attrs=dict(
                name='FooProp', value=CIMINSTANCE_C1_OBJ, type='string',
                embedded_object='instance',
            )
        ),
        None, None, True
    ),

    # embedded_object=False tests
    (
        "Verify that embedded_object=False results in None",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value='abc', type='string',
                embedded_object=False,
            ),
            exp_attrs=dict(
                name='FooProp', value='abc', type='string',
                embedded_object=None,
            )
        ),
        None, None, True
    ),

    # Is_array/array_size tests: Array tests with different is_array and
    # array_size
    (
        "Verify that is_array 42 is converted to bool (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=['abc'], type='string', is_array=42,
            ),
            exp_attrs=dict(
                name='FooProp', value=['abc'], type='string', is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array 'false' is converted to bool using Python "
        "bool rules(since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=['abc'], type='string',
                is_array='false',
            ),
            exp_attrs=dict(
                name='FooProp', value=['abc'], type='string',
                is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that unspecified is_array is implied to scalar with "
        "None value and type string",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=None, type='string',
            ),
            exp_attrs=dict(
                name='FooProp', value=None, type='string',
                is_array=False,
            )
        ),
        None, None, True
    ),
    (
        "Verify that unspecified is_array is implied to scalar with "
        "scalar string value but without type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value='abc', type='string',
            ),
            exp_attrs=dict(
                name='FooProp', value='abc', type='string',
                is_array=False,
            )
        ),
        None, None, True
    ),
    (
        "Verify that unspecified is_array is implied to array with "
        "array value",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=['abc'], type='string',
            ),
            exp_attrs=dict(
                name='FooProp', value=['abc'], type='string',
                is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that array_size 2 remains int",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=['abc'], type='string',
                is_array=True, array_size=2,
            ),
            exp_attrs=dict(
                name='FooProp', value=['abc'], type='string',
                is_array=True, array_size=2,
            )
        ),
        None, None, True
    ),
    (
        "Verify that mismatch between is_array False and array_size is "
        "ignored",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value='abc', type='string',
                is_array=False, array_size=2,
            ),
            exp_attrs=dict(
                name='FooProp', value='abc', type='string',
                is_array=False, array_size=2,
            )
        ),
        None, None, True
    ),

    # Value/type tests with arrays: Initialization to arrays of CIM string
    # and numeric types
    (
        "Verify that value None is permitted for string array",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=None, type='string', is_array=True,
            ),
            exp_attrs=dict(
                name='FooProp', value=None, type='string', is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that value empty list is permitted for string array",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[], type='string', is_array=True,
            ),
            exp_attrs=dict(
                name='FooProp', value=[], type='string', is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array is implied from value empty list",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[], type='string',
            ),
            exp_attrs=dict(
                name='FooProp', value=[], type='string', is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that value list(None) is permitted for string array",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[None], type='string', is_array=True,
            ),
            exp_attrs=dict(
                name='FooProp', value=[None], type='string', is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array is implied from value list(None)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[None], type='string',
            ),
            exp_attrs=dict(
                name='FooProp', value=[None], type='string', is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that type string is implied from value list(string)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=['abc'],
                is_array=True,
            ),
            exp_attrs=dict(
                name='FooProp', value=['abc'], type=exp_type_string,
                is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array is implied from type string and value "
        "list(string)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=['abc'], type='string',
            ),
            exp_attrs=dict(
                name='FooProp', value=['abc'], type='string',
                is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that type string and is_array are implied from value "
        "list(string)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=['abc'],
            ),
            exp_attrs=dict(
                name='FooProp', value=['abc'], type=exp_type_string,
                is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that type uint8 is implied from value list(uint8)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[Uint8(42)],
                is_array=True,
            ),
            exp_attrs=dict(
                name='FooProp', value=[Uint8(42)], type=exp_type_uint8,
                is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array is implied from type uint8 and value "
        "list(uint8)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[Uint8(42)], type='uint8',
            ),
            exp_attrs=dict(
                name='FooProp', value=[Uint8(42)], type='uint8',
                is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that type uint8 and is_array are implied from value "
        "list(uint8)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[Uint8(42)],
            ),
            exp_attrs=dict(
                name='FooProp', value=[Uint8(42)], type=exp_type_uint8,
                is_array=True,
            )
        ),
        None, None, True
    ),

    # Value/type tests with arrays: Initialization to arrays of CIM boolean
    # type
    (
        "Verify that is_array is implied from value empty list",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[], type='boolean',
            ),
            exp_attrs=dict(
                name='FooProp', value=[], type='boolean', is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array is implied from value list(None)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[None], type='boolean',
            ),
            exp_attrs=dict(
                name='FooProp', value=[None], type='boolean', is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that type boolean and is_array are implied from value "
        "list(boolean)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[True],
            ),
            exp_attrs=dict(
                name='FooProp', value=[True], type=exp_type_boolean,
                is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array is implied from type boolean and value "
        "list(boolean)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[False], type='boolean',
            ),
            exp_attrs=dict(
                name='FooProp', value=[False], type='boolean',
                is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that type boolean and is_array are implied from value "
        "list(boolean)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[True],
            ),
            exp_attrs=dict(
                name='FooProp', value=[True], type=exp_type_boolean,
                is_array=True,
            )
        ),
        None, None, True
    ),

    # Value/type tests with arrays: Initialization to arrays of CIM
    # datetime type
    (
        "Verify that type datetime and is_array are implied from value "
        "list(timedelta) (since 0.8.1)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[TIMEDELTA1_TD],
            ),
            exp_attrs=dict(
                name='FooProp', value=[TIMEDELTA1_OBJ],
                type=exp_type_datetime, is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array is implied from type datetime and value "
        "list(timedelta) (since 0.8.1)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[TIMEDELTA1_TD],
                type='datetime',
            ),
            exp_attrs=dict(
                name='FooProp', value=[TIMEDELTA1_OBJ],
                type='datetime', is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that type datetime and is_array are implied from value "
        "list(CIMDateTime)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[TIMEDELTA1_OBJ],
            ),
            exp_attrs=dict(
                name='FooProp', value=[TIMEDELTA1_OBJ],
                type=exp_type_datetime, is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array is implied from type datetime and value "
        "list(CIMDateTime)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[TIMEDELTA1_OBJ],
                type='datetime',
            ),
            exp_attrs=dict(
                name='FooProp', value=[TIMEDELTA1_OBJ],
                type='datetime', is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that type datetime and is_array are implied from value "
        "list(datetime) (since 0.8.1)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[DATETIME1_DT],
            ),
            exp_attrs=dict(
                name='FooProp', value=[DATETIME1_OBJ],
                type=exp_type_datetime, is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array is implied from type datetime and value "
        "list(datetime) (since 0.8.1)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[DATETIME1_DT],
                type='datetime',
            ),
            exp_attrs=dict(
                name='FooProp', value=[DATETIME1_OBJ],
                type='datetime', is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that type datetime and is_array are implied from value "
        "list(CIMDateTime)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[DATETIME1_OBJ],
            ),
            exp_attrs=dict(
                name='FooProp', value=[DATETIME1_OBJ],
                type=exp_type_datetime, is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array is implied from type datetime and value "
        "list(CIMDateTime)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[DATETIME1_OBJ],
                type='datetime',
            ),
            exp_attrs=dict(
                name='FooProp', value=[DATETIME1_OBJ],
                type='datetime', is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array is implied from type datetime and value "
        "list(None)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[None],
                type='datetime',
            ),
            exp_attrs=dict(
                name='FooProp', value=[None],
                type='datetime', is_array=True,
            )
        ),
        None, None, True
    ),

    # Value/type/embedded_object tests with arrays: Initialization to
    # arrays of CIM embedded objects (class and inst)
    (
        "Verify that is_array is implied from type string, "
        "embedded_object, for value list(None)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[None], type='string',
                embedded_object='object',
            ),
            exp_attrs=dict(
                name='FooProp', value=[None], type='string',
                embedded_object='object', is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array is implied from type string, "
        "embedded_object, for value list()",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[], type='string',
                embedded_object='object',
            ),
            exp_attrs=dict(
                name='FooProp', value=[], type='string',
                embedded_object='object', is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array, type and embedded_object are implied "
        "from value list(CIMClass)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[CIMCLASS_C1_OBJ],
            ),
            exp_attrs=dict(
                name='FooProp', value=[CIMCLASS_C1_OBJ], type=exp_type_string,
                embedded_object=exp_eo_object, is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array and embedded_object are implied from type "
        "and value list(CIMClass) (since 0.8.1)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[CIMCLASS_C1_OBJ], type='string',
            ),
            exp_attrs=dict(
                name='FooProp', value=[CIMCLASS_C1_OBJ], type='string',
                embedded_object=exp_eo_object, is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array and type are implied from embedded_object "
        "and value list(CIMClass)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[CIMCLASS_C1_OBJ],
                embedded_object='object',
            ),
            exp_attrs=dict(
                name='FooProp', value=[CIMCLASS_C1_OBJ], type=exp_type_string,
                embedded_object='object', is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array is implied from type, embedded_object "
        "and value list(CIMClass)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[CIMCLASS_C1_OBJ], type='string',
                embedded_object='object',
            ),
            exp_attrs=dict(
                name='FooProp', value=[CIMCLASS_C1_OBJ], type='string',
                embedded_object='object', is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array and type are implied from embedded_object "
        "and value list(CIMInstance)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp',
                value=[CIMINSTANCE_C1_OBJ],
                embedded_object='object',
            ),
            exp_attrs=dict(
                name='FooProp',
                value=[CIMINSTANCE_C1_OBJ],
                type=exp_type_string,
                embedded_object='object', is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array is implied from type, embedded_object "
        "and value list(CIMInstance)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[CIMINSTANCE_C1_OBJ], type='string',
                embedded_object='object',
            ),
            exp_attrs=dict(
                name='FooProp', value=[CIMINSTANCE_C1_OBJ], type='string',
                embedded_object='object', is_array=True,
            )
        ),
        None, None, True
    ),

    # Value/type/embedded_object tests with arrays: Initialization to
    # arrays of CIM embedded instances
    (
        "Verify that is_array, type and embedded_object are implied from "
        "value list(CIMInstance)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp',
                value=[CIMINSTANCE_C1_OBJ],
            ),
            exp_attrs=dict(
                name='FooProp',
                value=[CIMINSTANCE_C1_OBJ],
                type=exp_type_string,
                embedded_object=exp_eo_instance,
                is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array and type are implied from embedded_object "
        "and value list(CIMInstance)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp',
                value=[CIMINSTANCE_C1_OBJ],
                embedded_object='instance',
            ),
            exp_attrs=dict(
                name='FooProp',
                value=[CIMINSTANCE_C1_OBJ],
                type=exp_type_string,
                embedded_object='instance',
                is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array and embedded_object are implied from type "
        "and value list(CIMInstance) (since 0.8.1)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[CIMINSTANCE_C1_OBJ], type='string',
            ),
            exp_attrs=dict(
                name='FooProp', value=[CIMINSTANCE_C1_OBJ], type='string',
                embedded_object=exp_eo_instance, is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array is implied from type, embedded_object "
        "and value list(CIMInstance)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[CIMINSTANCE_C1_OBJ], type='string',
                embedded_object='instance',
            ),
            exp_attrs=dict(
                name='FooProp', value=[CIMINSTANCE_C1_OBJ], type='string',
                embedded_object='instance', is_array=True,
            )
        ),
        None, None, True
    ),

    # Check that the documented examples don't fail
    (
        "Verify documented example 1",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='MyNum', value=42, type='uint8',
            ),
            exp_attrs=dict(
                name='MyNum', value=Uint8(42), type='uint8',
            )
        ),
        None, None, True
    ),
    (
        "Verify documented example 2",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='MyNum', value=Uint8(42),
            ),
            exp_attrs=dict(
                name='MyNum', value=Uint8(42), type=exp_type_uint8,
            )
        ),
        None, None, True
    ),
    (
        "Verify documented example 3",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='MyNumArray', value=[1, 2, 3], type='uint8',
            ),
            exp_attrs=dict(
                name='MyNumArray', value=[Uint8(1), Uint8(2), Uint8(3)],
                type='uint8', is_array=True,
            )
        ),
        None, None, True
    ),
    (
        "Verify documented example 4",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='MyRef', value=CIMINSTANCENAME_C1_OBJ,
            ),
            exp_attrs=dict(
                name='MyRef', value=CIMINSTANCENAME_C1_OBJ,
                type=exp_type_reference,
            )
        ),
        None, None, True
    ),
    (
        "Verify documented example 5",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='MyEmbObj', value=CIMCLASS_C1_OBJ,
            ),
            exp_attrs=dict(
                name='MyEmbObj', value=CIMCLASS_C1_OBJ, type=exp_type_string,
                embedded_object=exp_eo_object,
            )
        ),
        None, None, True
    ),
    (
        "Verify documented example 6",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='MyEmbObj',
                value=CIMINSTANCE_C1_OBJ,
                embedded_object='object',
            ),
            exp_attrs=dict(
                name='MyEmbObj',
                value=CIMINSTANCE_C1_OBJ,
                type=exp_type_string,
                embedded_object='object',
            )
        ),
        None, None, True
    ),
    (
        "Verify documented example 7",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='MyEmbInst',
                value=CIMINSTANCE_C1_OBJ,
            ),
            exp_attrs=dict(
                name='MyEmbInst',
                value=CIMINSTANCE_C1_OBJ,
                type=exp_type_string,
                embedded_object=exp_eo_instance,
            )
        ),
        None, None, True
    ),
    (
        "Verify documented example 8",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='MyString', value=None, type='string',
            ),
            exp_attrs=dict(
                name='MyString', value=None, type='string',
            )
        ),
        None, None, True
    ),
    (
        "Verify documented example 9",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='MyNum', value=None, type='uint8',
            ),
            exp_attrs=dict(
                name='MyNum', value=None, type='uint8',
            )
        ),
        None, None, True
    ),
    (
        "Verify documented example 10",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='MyRef', value=None, type='reference',
                reference_class='MyClass',
            ),
            exp_attrs=dict(
                name='MyRef', value=None, type='reference',
                reference_class='MyClass',
            )
        ),
        None, None, True
    ),
    (
        "Verify documented example 11",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='MyEmbObj', value=None, type='string',
                embedded_object='object',
            ),
            exp_attrs=dict(
                name='MyEmbObj', value=None, type='string',
                embedded_object='object',
            )
        ),
        None, None, True
    ),
    (
        "Verify documented example 12",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='MyEmbInst', value=None, type='string',
                embedded_object='instance',
            ),
            exp_attrs=dict(
                name='MyEmbInst', value=None, type='string',
                embedded_object='instance',
            )
        ),
        None, None, True
    ),

    # Qualifiers tests
    (
        "Verify that qualifiers with item of invalid type fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='Prop1', value=None, type='string',
                qualifiers=[
                    'xxx_invalid_type'
                ]
            ),
            exp_attrs=None
        ),
        TypeError, None, True
    ),
    (
        "Verify qualifiers order preservation with list of CIMQualifier",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='Prop1', value=None, type='string',
                qualifiers=[
                    CIMQualifier('Q1', value='Ham'),
                    CIMQualifier('Q2', value='Cheese'),
                ],
            ),
            exp_attrs=dict(
                name='Prop1', value=None, type='string',
                qualifiers=NocaseDict([
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                    ('Q2', CIMQualifier('Q2', value='Cheese')),
                ]),
            )
        ),
        None, None, True
    ),
    (
        "Verify qualifiers order preservation with OrderedDict",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='Prop1', value=None, type='string',
                qualifiers=OrderedDict([
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                    ('Q2', CIMQualifier('Q2', value='Cheese')),
                ]),
            ),
            exp_attrs=dict(
                name='Prop1', value=None, type='string',
                qualifiers=NocaseDict([
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                    ('Q2', CIMQualifier('Q2', value='Cheese')),
                ]),
            )
        ),
        None, None, True
    ),
    (
        "Verify qualifiers order preservation with list of tuple(key,val)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='Prop1', value=None, type='string',
                qualifiers=[
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                    ('Q2', CIMQualifier('Q2', value='Cheese')),
                ],
            ),
            exp_attrs=dict(
                name='Prop1', value=None, type='string',
                qualifiers=NocaseDict([
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                    ('Q2', CIMQualifier('Q2', value='Cheese')),
                ]),
            )
        ),
        None, None, True
    ),
    (
        "Verify that qualifiers dict is converted to NocaseDict",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value='abc', type='string',
                qualifiers=dict(Q1=CIMQUALIFIER_Q1_OBJ),
            ),
            exp_attrs=dict(
                name='FooProp', value='abc', type='string',
                qualifiers=NocaseDict(Q1=CIMQUALIFIER_Q1_OBJ),
            )
        ),
        None, None, True
    ),
    (
        "Verify that qualifiers as list of CIMQualifier objects is "
        "converted to NocaseDict",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value='abc', type='string',
                qualifiers=[CIMQUALIFIER_Q1_OBJ],
            ),
            exp_attrs=dict(
                name='FooProp', value='abc', type='string',
                qualifiers=NocaseDict(Q1=CIMQUALIFIER_Q1_OBJ),
            )
        ),
        None, None, True
    ),
    (
        "Verify that qualifiers as NocaseDict stays NocaseDict",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value='abc', type='string',
                qualifiers=NocaseDict(Q1=CIMQUALIFIER_Q1_OBJ),
            ),
            exp_attrs=dict(
                name='FooProp', value='abc', type='string',
                qualifiers=NocaseDict(Q1=CIMQUALIFIER_Q1_OBJ),
            )
        ),
        None, None, True
    ),

    # Exception testcases
    (
        "Verify that name None fails (since 0.8.1)",
        dict(
            init_args=[],
            init_kwargs=dict(name=None, value='abc'),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that value None without type fails",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooProp', value=None),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that an invalid type fails (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooProp', value=None, type='xxx'),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that qualifier with inconsistent key / name fails "
        "(since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value='abc',
                qualifiers=dict(Q1_x=CIMQUALIFIER_Q1_OBJ),
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that type string for value None is not implied from "
        "embedded_object (before 0.8.1 and since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=None, embedded_object='object',
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that invalid embedded_object value fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=None, type='string',
                embedded_object='objectx'),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that invalid type for embedded_object fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=None, type='reference',
                embedded_object='object',
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that invalid value for embedded_object fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=CIMProperty('Boo', ''), type='string',
                embedded_object='object',
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that invalid array value for embedded_object fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[CIMProperty('Boo', '')], type='string',
                embedded_object='object',
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that a value of int without a type fails "
        "(raises ValueError instead of TypeError since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooProp', value=42),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that a value of float without a type fails "
        "(raises ValueError instead of TypeError since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooProp', value=42.1),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that arrays of reference properties are not allowed in "
        "CIM v2",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[None], type='reference',
                reference_class='CIM_Foo',
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that is_array and type string are not implied from "
        "embedded_object, for value list(None) (before 0.8.1 and "
        "since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[None], embedded_object='object',
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that is_array and type string are not implied from "
        "embedded_object, for value list() (before 0.8.1 and "
        "since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[], embedded_object='object',
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that value None witout type fails also for arrays",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooProp', value=None, is_array=True),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that value list() without type fails also for arrays",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooProp', value=[], is_array=True),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that value list(None) without type fails also for arrays",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooProp', value=[None], is_array=True),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that value list(None,str) without type fails also for "
        "arrays",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooProp', value=[None, 'abc'], is_array=True,
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that value list() without type fails also when "
        "is_array is unspecified",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooProp', value=[]),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that value list(None) without type fails also when "
        "is_array is unspecified",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooProp', value=[None]),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that value list(None,str) without type fails also when "
        "is_array is unspecified",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooProp', value=[None, 'abc']),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that value list(int) without type fails "
        "(raises ValueError instead of TypeError since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooProp', value=[1]),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that value list(float) without type fails "
        "(raises ValueError instead of TypeError since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooProp', value=[1.1]),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that value list(long) without type fails (Python 2 only) "
        "(raises ValueError instead of TypeError since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooProp', value=[1]),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMPROPERTY_INIT)
@simplified_test_function
@log_entry_exit
def test_CIMProperty_init(testcase, init_args, init_kwargs, exp_attrs):
    """
    Test function for CIMProperty.__init__()

    Note: The inferring of the type attribute of CIMProperty changed over
    time: Before pywbem 0.8.1, a value of None required specifying a
    type. In 0.8.1, a rather complex logic was implemented that tried to
    be perfect in inferring unspecified input properties, at the price of
    considerable complexity. In 0.12.0, this complex logic was
    drastically simplified again. Some cases are still nicely inferred,
    but some less common cases now require specifying the type again.
    """

    # The code to be tested
    obj = CIMProperty(*init_args, **init_kwargs)

    assert not hasattr(obj, '__dict__')

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

    exp_name = exp_attrs['name']
    assert obj.name == exp_name
    assert isinstance(obj.name, type(exp_name))

    exp_value = exp_attrs['value']
    assert obj.value == exp_value
    assert isinstance(obj.value, type(exp_value))

    exp_type = exp_attrs['type']
    assert obj.type == exp_type
    assert isinstance(obj.type, type(exp_type))

    exp_class_origin = exp_attrs.get('class_origin', None)
    assert obj.class_origin == exp_class_origin
    assert isinstance(obj.class_origin, type(exp_class_origin))

    exp_array_size = exp_attrs.get('array_size', None)
    assert obj.array_size == exp_array_size
    assert isinstance(obj.array_size, type(exp_array_size))

    exp_propagated = exp_attrs.get('propagated', None)
    assert obj.propagated == exp_propagated
    assert isinstance(obj.propagated, type(exp_propagated))

    exp_is_array = exp_attrs.get('is_array', False)
    assert obj.is_array == exp_is_array
    assert isinstance(obj.is_array, type(exp_is_array))

    exp_reference_class = exp_attrs.get('reference_class', None)
    assert obj.reference_class == exp_reference_class
    assert isinstance(obj.reference_class, type(exp_reference_class))

    exp_qualifiers = exp_attrs.get('qualifiers', NocaseDict())
    assert obj.qualifiers == exp_qualifiers
    assert isinstance(obj.qualifiers, type(exp_qualifiers))

    exp_embedded_object = exp_attrs.get('embedded_object', None)
    assert obj.embedded_object == exp_embedded_object
    assert isinstance(obj.embedded_object, type(exp_embedded_object))


TESTCASES_CIMPROPERTY_COPY = [

    # Testcases for CIMProperty.copy()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj_kwargs: Dict of init kwargs for original CIMProperty.
    # * 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

    (
        "Scalar property of type string, with qualifiers",
        dict(
            obj_kwargs=dict(
                name='PropFoo',
                value='Bar',
                type='string',
                is_array=False,
                array_size=None,
                class_origin=None,
                propagated=False,
                reference_class=None,
                embedded_object=None,
                qualifiers=[
                    CIMQualifier('Key', value=True),
                ],
            )
        ),
        None, None, True
    ),
    (
        "Scalar property of type uint64, no qualifiers",
        dict(
            obj_kwargs=dict(
                name='PropFoo',
                value=42,
                type='uint64',
                is_array=False,
                array_size=None,
                class_origin=None,
                propagated=False,
                reference_class=None,
                embedded_object=None,
                qualifiers=None,
            )
        ),
        None, None, True
    ),
    (
        "Scalar property of type datetime, no qualifiers",
        dict(
            obj_kwargs=dict(
                name='PropFoo',
                value='20191005155152.123456-120',
                type='datetime',
                is_array=False,
                array_size=None,
                class_origin=None,
                propagated=False,
                reference_class=None,
                embedded_object=None,
                qualifiers=None,
            )
        ),
        None, None, True
    ),
    (
        "Variable array property of type uint8, no qualifiers",
        dict(
            obj_kwargs=dict(
                name='PropFoo',
                value=[Uint8(42)],
                type='uint8',
                is_array=True,
                array_size=None,
                class_origin=None,
                propagated=False,
                reference_class=None,
                embedded_object=None,
                qualifiers=None,
            )
        ),
        None, None, True
    ),
    (
        "Fixed array property of type uint8, no qualifiers",
        dict(
            obj_kwargs=dict(
                name='PropFoo',
                value=[Uint8(42)],
                type='uint8',
                is_array=True,
                array_size=5,
                class_origin=None,
                propagated=False,
                reference_class=None,
                embedded_object=None,
                qualifiers=None,
            )
        ),
        None, None, True
    ),
    (
        "Scalar property of type reference, no qualifiers",
        dict(
            obj_kwargs=dict(
                name='PropFoo',
                value=CIMInstanceName('CIM_Foo'),
                type='reference',
                is_array=False,
                array_size=None,
                class_origin=None,
                propagated=False,
                reference_class='CIM_Foo',
                embedded_object=None,
                qualifiers=None,
            )
        ),
        None, None, True
    ),
    (
        "Scalar property of type string that is embedded object with "
        "instance value, no qualifiers",
        dict(
            obj_kwargs=dict(
                name='PropFoo',
                value=CIMInstance('CIM_Foo'),
                type='string',
                is_array=False,
                array_size=None,
                class_origin=None,
                propagated=False,
                reference_class=None,
                embedded_object='object',
                qualifiers=None,
            )
        ),
        None, None, True
    ),
    (
        "Scalar property of type string that is embedded object with "
        "class value, no qualifiers",
        dict(
            obj_kwargs=dict(
                name='PropFoo',
                value=CIMClass('CIM_Foo'),
                type='string',
                is_array=False,
                array_size=None,
                class_origin=None,
                propagated=False,
                reference_class=None,
                embedded_object='object',
                qualifiers=None,
            )
        ),
        None, None, True
    ),
    (
        "Scalar property of type string that is embedded instance with "
        "instance value, no qualifiers",
        dict(
            obj_kwargs=dict(
                name='PropFoo',
                value=CIMInstance('CIM_Foo'),
                type='string',
                is_array=False,
                array_size=None,
                class_origin=None,
                propagated=False,
                reference_class=None,
                embedded_object='instance',
                qualifiers=None,
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with propagated and class_origin set, no qualifiers",
        dict(
            obj_kwargs=dict(
                name='PropFoo',
                value='Bar',
                type='string',
                is_array=False,
                array_size=None,
                class_origin='CIMFooParent',
                propagated=True,
                reference_class=None,
                embedded_object=None,
                qualifiers=None,
            )
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMPROPERTY_COPY)
@simplified_test_function
@log_entry_exit
def test_CIMProperty_copy(testcase, obj_kwargs):
    """
    Test function for CIMProperty.copy()
    """

    obj1 = CIMProperty(**obj_kwargs)

    # The code to be tested
    obj2 = obj1.copy()

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

    # Verify that the copy is equal to the original (performs deep equality)
    assert obj2 == obj1

    # Verify that the copy is a different object
    assert id(obj2) != id(obj1)

    # TODO: Decide whether mutable child objects should be copied, and add test:
    # # Verify that the mutable child objects are different objects
    # if not isinstance(obj1.value, (int, str)):
    #     assert id(obj2.value) != id(obj1.value)

    # Verify that qualifiers are shallow-copied (see CIMProperty.copy())
    if obj1.qualifiers is not None:
        assert id(obj2.qualifiers) != id(obj1.qualifiers)
        for qual_name in obj1.qualifiers:
            qual1 = obj1.qualifiers[qual_name]
            qual2 = obj2.qualifiers[qual_name]
            assert id(qual1) == id(qual2)

    # Verify that the copy can be modified and the original remains unchanged.
    # Most of the attribute setters don't validate the change, because multiple
    # such changes might be needed to make the object consistent again.

    obj1_name = obj1.name
    obj2.name = 'SomeNewProp'
    assert obj1.name == obj1_name

    # TODO: Decide whether value objects should be copied, and add test:
    # obj1_value = obj1.value
    # obj2.value = None  # avoid going through the types
    # assert obj1.value == obj1_value

    obj1_type = obj1.type
    obj2.type = 'uint8' if obj1.type == 'string' else 'string'
    assert obj1.type == obj1_type

    obj1_is_array = obj1.is_array
    obj2.is_array = not obj1.is_array
    assert obj1.is_array == obj1_is_array

    if obj1.is_array:
        obj1_array_size = obj1.array_size
        obj2.array_size = 10 if obj1.array_size is None else None
        assert obj1.array_size == obj1_array_size

    obj1_class_origin = obj1.class_origin
    obj2.class_origin = 'SomeNewClass' if obj1.class_origin is None else None
    assert obj1.class_origin == obj1_class_origin

    obj1_propagated = obj1.propagated
    obj2.propagated = not obj1.propagated
    assert obj1.propagated == obj1_propagated

    if obj1.type == 'reference':
        obj1_reference_class = obj1.reference_class
        obj2.reference_class = 'SomeNewClass'
        assert obj1.reference_class == obj1_reference_class

    if obj1.embedded_object is not None:
        obj1_embedded_object = obj1.embedded_object
        obj2.reference_class = 'object' \
            if obj1.embedded_object == 'instance' else 'instance'
        assert obj1.embedded_object == obj1_embedded_object

    obj1_qualifiers = obj1.qualifiers
    obj2.qualifiers = [CIMQualifier('SomeNewQualifier', value=True)]
    assert obj1.qualifiers == obj1_qualifiers


CIMPROPERTY_SETATTR_P1_KWARGS = dict(
    name='P1',
    value='V1',
    type='string',
    reference_class=None,
    embedded_object=None,
    is_array=False,
    array_size=None,
    propagated=None,
    class_origin=None,
    qualifiers=dict(Q1=CIMQualifier('Q1', False)),
)

TESTCASES_CIMPROPERTY_SETATTR = [

    # Testcases for CIMProperty set attribute

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj_kwargs: Dict of init kwargs for CIMProperty.
    #   * item: Name of CIMProperty attr to set, or tuple
    #     (item, key) to set a single item in an attr that is a dict.
    #   * new_value: New value to set.
    #   * exp_attrs: Dict of expected attributes, for validation.
    # * 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

    # Tests that set the name attribute
    (
        "Set name to different string",
        dict(
            obj_kwargs=CIMPROPERTY_SETATTR_P1_KWARGS,
            item='name',
            new_value='P2',
            exp_attrs=dict(
                name='P2',
            ),
        ),
        None, None, True
    ),
    (
        "Set name to None",
        # Before 0.12.0, the implementation allowed the name to be None,
        # although the documentation required it not to be None.
        # We test the implemented behavior. Since 0.12.0, this raises
        # ValueError.
        dict(
            obj_kwargs=CIMPROPERTY_SETATTR_P1_KWARGS,
            item='name',
            new_value=None,
            exp_attrs=dict(
                name=None,
            ),
        ),
        # raises ValueError since 0.12
        ValueError, None, True
    ),

    # Tests that set the value attribute for scalar string types
    (
        "For scalar string type, set value to 7-bit ASCII unicode string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='value',
            new_value='V2',
            exp_attrs=dict(
                value='V2',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set value to 7-bit ASCII byte string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='value',
            new_value=b'V2',
            exp_attrs=dict(
                value='V2',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set value to non-UCS-2 unicode string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='value',
            new_value='Foo\U00010142',
            exp_attrs=dict(
                value='Foo\U00010142',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set value to non-UCS-2 UTF-8 byte string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='value',
            new_value=b'Foo\xF0\x90\x85\x82',
            exp_attrs=dict(
                value='Foo\U00010142',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for scalar char16 types
    (
        "For scalar char16 type, set value to 7-bit ASCII Char16 string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value=Char16('U'),
            exp_attrs=dict(
                value='U',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar char16 type, set value to 7-bit ASCII unicode string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value='U',
            exp_attrs=dict(
                value='U',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar char16 type, set value to 7-bit ASCII byte string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value=b'U',
            exp_attrs=dict(
                value='U',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar char16 type, set value to non-UCS-2 Char16 string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value=Char16('\U00010142'),
            exp_attrs=dict(
                value='\U00010142',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar char16 type, set value to non-UCS-2 unicode string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value='\U00010142',
            exp_attrs=dict(
                value='\U00010142',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar char16 type, set value to non-UCS-2 UTF-8 byte string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value=b'\xF0\x90\x85\x82',
            exp_attrs=dict(
                value='\U00010142',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar char16 type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for scalar boolean type
    (
        "For scalar boolean type, set value to boolean True",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=False,
                type='boolean',
            ),
            item='value',
            new_value=True,
            exp_attrs=dict(
                value=True,
            ),
        ),
        None, None, True
    ),
    (
        "For scalar boolean type, set value to boolean False",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=True,
                type='boolean',
            ),
            item='value',
            new_value=False,
            exp_attrs=dict(
                value=False,
            ),
        ),
        None, None, True
    ),
    (
        "For scalar boolean type, set value to string 'true'",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=False,
                type='boolean',
            ),
            item='value',
            new_value='true',
            exp_attrs=dict(
                value=True,
            ),
        ),
        None, None, True
    ),
    (
        "For scalar boolean type, set value to string 'false'",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=False,
                type='boolean',
            ),
            item='value',
            new_value='false',
            exp_attrs=dict(
                value=True,  # no processing of 'true'/'false' strings
            ),
        ),
        None, None, True
    ),
    (
        "For scalar boolean type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=False,
                type='boolean',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for scalar integer types
    (
        "For scalar uint8 type, set value to integer out of range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=42,
                type='uint8',
            ),
            item='value',
            new_value=256,
            exp_attrs=dict(
                value=None,
            ),
        ),
        ValueError, None, True  # out of range
    ),
    (
        "For scalar uint64 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=42,
                type='uint64',
            ),
            item='value',
            new_value=12345678901234,
            exp_attrs=dict(
                value=12345678901234,
            ),
        ),
        None, None, True
    ),
    (
        "For scalar sint8 type, set value to integer out of range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=42,
                type='sint8',
            ),
            item='value',
            new_value=-129,
            exp_attrs=dict(
                value=None,
            ),
        ),
        ValueError, None, True  # out of range
    ),
    (
        "For scalar sint64 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=42,
                type='sint64',
            ),
            item='value',
            new_value=-12345678901234,
            exp_attrs=dict(
                value=-12345678901234,
            ),
        ),
        None, None, True
    ),
    (
        "For scalar sint32 type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=42,
                type='sint32',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for scalar real types
    (
        "For scalar real32 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=42.1,
                type='real32',
            ),
            item='value',
            new_value=-12345678890.1,
            exp_attrs=dict(
                value=-12345678890.1,
            ),
        ),
        None, None, True
    ),
    (
        "For scalar real64 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=42.1,
                type='real64',
            ),
            item='value',
            new_value=-12345678890.1,
            exp_attrs=dict(
                value=-12345678890.1,
            ),
        ),
        None, None, True
    ),
    (
        "For scalar real64 type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=42,
                type='real64',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for scalar reference type
    (
        "For scalar reference type, set value to CIMInstanceName",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=CIMINSTANCENAME_C1_OBJ,
                type='reference',
                reference_class='C1',
            ),
            item='value',
            new_value=CIMInstanceName('C2'),
            exp_attrs=dict(
                value=CIMInstanceName('C2'),
                reference_class='C1',  # not set automatically
            ),
        ),
        None, None, True
    ),
    (
        "For scalar reference type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=CIMINSTANCENAME_C1_OBJ,
                type='reference',
                reference_class='C1',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
                reference_class='C1',  # not set automatically
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for scalar datetime type
    (
        "For scalar datetime type, set value to datetime object",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value=datetime(
                year=2019, month=10, day=20, hour=15, minute=30, second=40,
                microsecond=654321, tzinfo=MinutesFromUTC(120)
            ),
            exp_attrs=dict(
                value=CIMDateTime(datetime(
                    year=2019, month=10, day=20, hour=15, minute=30, second=40,
                    microsecond=654321, tzinfo=MinutesFromUTC(120)
                )),
            ),
        ),
        None, None, True
    ),
    (
        "For scalar datetime type, set value to point in time string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value='20191020153040.654321+120',
            exp_attrs=dict(
                value=CIMDateTime('20191020153040.654321+120'),
            ),
        ),
        None, None, True
    ),
    (
        "For scalar datetime type, set value to point in time CIMDateTime",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value=CIMDateTime('20191020153040.654321+120'),
            exp_attrs=dict(
                value=CIMDateTime('20191020153040.654321+120'),
            ),
        ),
        None, None, True
    ),
    (
        "For scalar datetime type, set value to timedelta object",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value=timedelta(25, (14 * 60 + 24) * 60 + 41, 234567),
            exp_attrs=dict(
                value=CIMDateTime(
                    timedelta(25, (14 * 60 + 24) * 60 + 41, 234567)
                ),
            ),
        ),
        None, None, True
    ),
    (
        "For scalar datetime type, set value to interval string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value='00000173232441.234567:000',
            exp_attrs=dict(
                value=CIMDateTime('00000173232441.234567:000'),
            ),
        ),
        None, None, True
    ),
    (
        "For scalar datetime type, set value to interval CIMDateTime",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value=CIMDateTime('00000173232441.234567:000'),
            exp_attrs=dict(
                value=CIMDateTime('00000173232441.234567:000'),
            ),
        ),
        None, None, True
    ),
    (
        "For scalar datetime type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for scalar embedded objects
    (
        "For scalar string type, set value to embedded instance",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='X',
                type='string',
                embedded_object=None,
            ),
            item='value',
            new_value=CIMInstance('C2'),
            exp_attrs=dict(
                value=CIMInstance('C2'),
                embedded_object=None,  # not set automatically
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set value to embedded class",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='X',
                type='string',
                embedded_object=None,
            ),
            item='value',
            new_value=CIMClass('C2'),
            exp_attrs=dict(
                value=CIMClass('C2'),
                embedded_object=None,  # not set automatically
            ),
        ),
        None, None, True
    ),

    # Tests that set array value on scalar property and vice versa
    (
        "For array string type, set value to scalar string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value='V2',
            exp_attrs=dict(
                value='V2',  # scalar/array mismatch not checked
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set value to array of strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='value',
            new_value=['V2'],
            exp_attrs=dict(
                value=['V2'],  # scalar/array mismatch not checked
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for array string types
    (
        "For array string type, set value to array of 7-bit ASCII unicode "
        "strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['V2', 'V3'],
            exp_attrs=dict(
                value=['V2', 'V3'],
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set value to array of 7-bit ASCII byte "
        "strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[b'V2', b'V3'],
            exp_attrs=dict(
                value=['V2', 'V3'],
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set value to array of non-UCS-2 unicode "
        "strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['Foo\U00010142'],
            exp_attrs=dict(
                value=['Foo\U00010142'],
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set value to array of non-UCS-2 UTF-8 byte "
        "strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[b'Foo\xF0\x90\x85\x82'],
            exp_attrs=dict(
                value=['Foo\U00010142'],
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set value to empty array",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[],
            exp_attrs=dict(
                value=[],
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set value to array of one None item",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[None],
            exp_attrs=dict(
                value=[None],
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),
    (
        "For fixed array string type, set value to max items",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=2,
            ),
            item='value',
            new_value=['V1', 'V2'],
            exp_attrs=dict(
                value=['V1', 'V2'],
            ),
        ),
        None, None, True
    ),
    (
        "For fixed array string type, set value to one more than max items",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=2,
            ),
            item='value',
            new_value=['V1', 'V2', 'V3'],
            exp_attrs=dict(
                value=['V1', 'V2', 'V3'],  # fixed size not checked
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for array char16 types
    (
        "For array char16 type, set value to array of 7-bit ASCII Char16 "
        "strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[Char16('U'), Char16('V')],
            exp_attrs=dict(
                value=['U', 'V'],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to array of 7-bit ASCII unicode "
        "strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['U', 'V'],
            exp_attrs=dict(
                value=['U', 'V'],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to array of 7-bit ASCII byte "
        "strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[b'U', b'V'],
            exp_attrs=dict(
                value=['U', 'V'],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to array of non-UCS-2 Char16 "
        "strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[Char16('\U00010142')],
            exp_attrs=dict(
                value=['\U00010142'],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to array of non-UCS-2 unicode "
        "strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['\U00010142'],
            exp_attrs=dict(
                value=['\U00010142'],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to array of non-UCS-2 UTF-8 byte "
        "strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[b'\xF0\x90\x85\x82'],
            exp_attrs=dict(
                value=['\U00010142'],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to empty array",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[],
            exp_attrs=dict(
                value=[],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to array of one None item",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[None],
            exp_attrs=dict(
                value=[None],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for array boolean type
    (
        "For array boolean type, set value to array of boolean True",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[False],
                type='boolean',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[True, False],
            exp_attrs=dict(
                value=[True, False],
            ),
        ),
        None, None, True
    ),
    (
        "For array boolean type, set value to array of boolean False",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[True],
                type='boolean',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[False, True],
            exp_attrs=dict(
                value=[False, True],
            ),
        ),
        None, None, True
    ),
    (
        "For array boolean type, set value to array of string 'true'",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[False],
                type='boolean',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['true'],
            exp_attrs=dict(
                value=[True],
            ),
        ),
        None, None, True
    ),
    (
        "For array boolean type, set value to array of string 'false'",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[False],
                type='boolean',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['false'],
            exp_attrs=dict(
                value=[True],  # no processing of 'true'/'false' strings
            ),
        ),
        None, None, True
    ),
    (
        "For array boolean type, set value to empty array",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[False],
                type='boolean',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[],
            exp_attrs=dict(
                value=[],
            ),
        ),
        None, None, True
    ),
    (
        "For array boolean type, set value to array of one None item",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[False],
                type='boolean',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[None],
            exp_attrs=dict(
                value=[None],
            ),
        ),
        None, None, True
    ),
    (
        "For array boolean type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[False],
                type='boolean',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for array integer types
    (
        "For array uint8 type, set value to integer out of range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42],
                type='uint8',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[256],
            exp_attrs=None,
        ),
        ValueError, None, True  # out of range
    ),
    (
        "For array uint64 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42],
                type='uint64',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[12345678901234],
            exp_attrs=dict(
                value=[12345678901234],
            ),
        ),
        None, None, True
    ),
    (
        "For array sint8 type, set value to integer out of range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42],
                type='sint8',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[-129],
            exp_attrs=None,
        ),
        ValueError, None, True  # out of range
    ),
    (
        "For array sint64 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42],
                type='sint64',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[-12345678901234],
            exp_attrs=dict(
                value=[-12345678901234],
            ),
        ),
        None, None, True
    ),
    (
        "For array sint32 type, set value to empty array",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42],
                type='sint32',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[],
            exp_attrs=dict(
                value=[],
            ),
        ),
        None, None, True
    ),
    (
        "For array sint32 type, set value to array of one None item",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42],
                type='sint32',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[None],
            exp_attrs=dict(
                value=[None],
            ),
        ),
        None, None, True
    ),
    (
        "For array sint32 type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42],
                type='sint32',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for array real types
    (
        "For array real32 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42.1],
                type='real32',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[-12345678890.1],
            exp_attrs=dict(
                value=[-12345678890.1],
            ),
        ),
        None, None, True
    ),
    (
        "For array real64 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42.1],
                type='real64',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[-12345678890.1],
            exp_attrs=dict(
                value=[-12345678890.1],
            ),
        ),
        None, None, True
    ),
    (
        "For array real64 type, set value to empty array",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42],
                type='real64',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[],
            exp_attrs=dict(
                value=[],
            ),
        ),
        None, None, True
    ),
    (
        "For array real64 type, set value to array of one None item",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42],
                type='real64',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[None],
            exp_attrs=dict(
                value=[None],
            ),
        ),
        None, None, True
    ),
    (
        "For array real64 type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42],
                type='real64',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Note: References cannot be arrays - tests are in test_CIMProperty_init()

    # Tests that set the value attribute for array datetime type
    (
        "For array datetime type, set value to array of datetime objects",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[datetime(
                year=2019, month=10, day=20, hour=15, minute=30, second=40,
                microsecond=654321, tzinfo=MinutesFromUTC(120)
            )],
            exp_attrs=dict(
                value=[CIMDateTime(datetime(
                    year=2019, month=10, day=20, hour=15, minute=30, second=40,
                    microsecond=654321, tzinfo=MinutesFromUTC(120)
                ))],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to array of point in time strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['20191020153040.654321+120'],
            exp_attrs=dict(
                value=[CIMDateTime('20191020153040.654321+120')],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to array of point in time "
        "CIMDateTime objects",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[CIMDateTime('20191020153040.654321+120')],
            exp_attrs=dict(
                value=[CIMDateTime('20191020153040.654321+120')],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to array of timedelta objects",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[timedelta(25, (14 * 60 + 24) * 60 + 41, 234567)],
            exp_attrs=dict(
                value=[CIMDateTime(
                    timedelta(25, (14 * 60 + 24) * 60 + 41, 234567)
                )],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to array of interval strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['00000173232441.234567:000'],
            exp_attrs=dict(
                value=[CIMDateTime('00000173232441.234567:000')],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to array of interval CIMDateTime"
        "objects",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[CIMDateTime('00000173232441.234567:000')],
            exp_attrs=dict(
                value=[CIMDateTime('00000173232441.234567:000')],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to empty array",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[],
            exp_attrs=dict(
                value=[],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to array of one None item",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[None],
            exp_attrs=dict(
                value=[None],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for array embedded objects
    (
        "For array string type, set value to embedded instance",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['X'],
                type='string',
                embedded_object=None,
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[CIMInstance('C2')],
            exp_attrs=dict(
                value=[CIMInstance('C2')],
                embedded_object=None,  # not set automatically
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set value to embedded class",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['X'],
                type='string',
                embedded_object=None,
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[CIMClass('C2')],
            exp_attrs=dict(
                value=[CIMClass('C2')],
                embedded_object=None,  # not set automatically
            ),
        ),
        None, None, True
    ),

    # Tests that set the type attribute
    (
        "For scalar string type, set type to uint8",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='type',
            new_value='uint8',
            exp_attrs=dict(
                type='uint8',
                value='V1',
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set type to uint8",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='type',
            new_value='uint8',
            exp_attrs=dict(
                type='uint8',
                value=['V1'],
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set type to invalid type",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='type',
            new_value='xxx',
            exp_attrs=None,
        ),
        ValueError, None, True  # invalid type
    ),
    (
        "For scalar string type, set type to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='type',
            new_value=None,
            exp_attrs=None,
        ),
        ValueError, None, True  # invalid type
    ),

    # Tests that set the reference_class attribute
    (
        "For string type, set reference_class to a class name string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='reference_class',
            new_value='C2',
            exp_attrs=dict(
                type='string',  # inconsistency with type not checked
                value='V1',
                reference_class='C2',
            ),
        ),
        None, None, True
    ),
    (
        "For reference type, set reference_class to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=CIMInstanceName('C1'),
                type='reference',
                reference_class='C1',
            ),
            item='reference_class',
            new_value=None,
            exp_attrs=dict(
                type='reference',  # inconsistency with type not checked
                value=CIMInstanceName('C1'),
                reference_class=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the embedded_object attribute
    (
        "For string type, set embedded_object to 'instance'",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='embedded_object',
            new_value='instance',
            exp_attrs=dict(
                type='string',
                value='V1',
                embedded_object='instance',  # incons. with value not checked
            ),
        ),
        None, None, True
    ),
    (
        "For string type, set embedded_object to 'object'",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='embedded_object',
            new_value='object',
            exp_attrs=dict(
                type='string',
                value='V1',
                embedded_object='object',  # incons. with value not checked
            ),
        ),
        None, None, True
    ),
    (
        "For string type, set invalid embedded_object",
        # TODO: Clarify whether invalid embedded_object should be checked
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='embedded_object',
            new_value='xxx',
            exp_attrs=dict(
                type='string',
                value='V1',
                embedded_object='xxx',
            ),
        ),
        None, None, True
    ),
    (
        "For embedded instance, set embedded_object to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=CIMInstance('C1'),
                type='string',
                embedded_object='instance',
            ),
            item='embedded_object',
            new_value=None,
            exp_attrs=dict(
                type='string',
                value=CIMInstance('C1'),
                embedded_object=None,  # incons. with value not checked
            ),
        ),
        None, None, True
    ),

    # Tests that set the is_array attribute
    (
        "For scalar string type, set is_array to True",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
                is_array=False,
                array_size=None,
            ),
            item='is_array',
            new_value=True,
            exp_attrs=dict(
                type='string',
                value='V1',
                is_array=True,  # inconsistency with value not checked
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set is_array to 'false' "
        "(for boolean conversion to True)",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
                is_array=False,
                array_size=None,
            ),
            item='is_array',
            new_value='false',
            exp_attrs=dict(
                type='string',
                value='V1',
                is_array=True,  # inconsistency with value not checked
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set is_array to False",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='is_array',
            new_value=False,
            exp_attrs=dict(
                type='string',
                value=['V1'],
                is_array=False,  # inconsistency with value not checked
            ),
        ),
        None, None, True
    ),

    # Tests that set the array_size attribute
    (
        "For scalar string type, set array_size to 2",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
                is_array=False,
                array_size=None,
            ),
            item='array_size',
            new_value=2,
            exp_attrs=dict(
                type='string',
                value='V1',
                is_array=False,
                array_size=2,  # inconsistency with is_array not checked
            ),
        ),
        None, None, True
    ),
    (
        "For variable array string type, set array_size to 2",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='array_size',
            new_value=2,
            exp_attrs=dict(
                type='string',
                value=['V1'],
                is_array=True,
                array_size=2,
            ),
        ),
        None, None, True
    ),
    (
        "For fixed size 5 array string type with one item, set array_size to "
        "smaller size 2",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=5,
            ),
            item='array_size',
            new_value=2,
            exp_attrs=dict(
                type='string',
                value=['V1'],
                is_array=True,
                array_size=2,
            ),
        ),
        None, None, True
    ),
    (
        "For fixed size 5 array string type with 5 items, set array_size to "
        "too small size 2",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1', 'V2', 'V3', 'V4', 'V5'],
                type='string',
                is_array=True,
                array_size=5,
            ),
            item='array_size',
            new_value=2,
            exp_attrs=dict(
                type='string',
                value=['V1', 'V2', 'V3', 'V4', 'V5'],
                is_array=True,
                array_size=2,  # inconsistency with actual usage not checked
            ),
        ),
        None, None, True
    ),

    # Tests that set the propagated attribute
    (
        "For non-propagated property without class origin, set propagated to "
        "True",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
                propagated=False,
                class_origin=None,
            ),
            item='propagated',
            new_value=True,
            exp_attrs=dict(
                propagated=True,  # inconsistency with class origin not checked
                class_origin=None,
            ),
        ),
        None, None, True
    ),
    (
        "For non-propagated property with class origin, set propagated to "
        "True",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
                propagated=False,
                class_origin='C2',
            ),
            item='propagated',
            new_value=True,
            exp_attrs=dict(
                propagated=True,
                class_origin='C2',
            ),
        ),
        None, None, True
    ),
    (
        "For non-propagated property with class origin, set propagated to "
        "'false' (for boolean conversion to True)",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
                propagated='false',
                class_origin='C2',
            ),
            item='propagated',
            new_value=True,
            exp_attrs=dict(
                propagated=True,
                class_origin='C2',
            ),
        ),
        None, None, True
    ),
    (
        "For propagated property with class origin, set propagated to False",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
                propagated=True,
                class_origin='C2',
            ),
            item='propagated',
            new_value=False,
            exp_attrs=dict(
                propagated=False,  # incons. with class origin not checked
                class_origin='C2',
            ),
        ),
        None, None, True
    ),

    # Tests that set the class_origin attribute
    (
        "For propagated property without class origin, set class origin",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
                propagated=True,
                class_origin=None,
            ),
            item='class_origin',
            new_value='C2',
            exp_attrs=dict(
                propagated=True,
                class_origin='C2',
            ),
        ),
        None, None, True
    ),
    (
        "For propagated property with class origin, set class origin to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
                propagated=True,
                class_origin='C2',
            ),
            item='class_origin',
            new_value=None,
            exp_attrs=dict(
                propagated=True,
                class_origin=None,  # incons. with propagated not checked
            ),
        ),
        None, None, True
    ),
    (
        "For non-propagated property without class origin, set class origin",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
                propagated=False,
                class_origin=None,
            ),
            item='class_origin',
            new_value='C2',
            exp_attrs=dict(
                propagated=False,
                class_origin='C2',  # incons. with propagated not checked
            ),
        ),
        None, None, True
    ),

    # Tests that set the qualifiers attribute
    (
        "Set qualifiers to new dict with CIMQualifier with correct name",
        dict(
            obj_kwargs=CIMPROPERTY_SETATTR_P1_KWARGS,
            item='qualifiers',
            new_value=dict(Q2=CIMQualifier('Q2', True)),
            exp_attrs=dict(
                qualifiers=dict(
                    Q2=CIMQualifier('Q2', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set qualifiers to new dict with CIMQualifier with incorrect name",
        dict(
            obj_kwargs=CIMPROPERTY_SETATTR_P1_KWARGS,
            item='qualifiers',
            new_value=dict(Q2=CIMQualifier('Q2x', True)),
            exp_attrs=None,
        ),
        # raises ValueError since 0.12
        ValueError, None, True  # inconsistent name
    ),
    (
        "Set qualifiers to new dict with CIMQualifier with name None",
        dict(
            obj_kwargs=CIMPROPERTY_SETATTR_P1_KWARGS,
            item='qualifiers',
            # Name of CIMQualifier object is not None, to get over that check.
            new_value=dict([(None, CIMQualifier('Qnone', True))]),
            exp_attrs=dict(
                qualifiers=dict([
                    ('Q1', CIMQualifier('Q1', False)),
                    (None, CIMQualifier('Qnone', True)),
                ]),
            ),
        ),
        # raises ValueError since 0.12
        ValueError, None, True
    ),
    (
        "Set qualifiers to new dict with simple value",
        dict(
            obj_kwargs=CIMPROPERTY_SETATTR_P1_KWARGS,
            item='qualifiers',
            new_value=dict(Q1=True),
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1', True)
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set qualifiers to None",
        dict(
            obj_kwargs=CIMPROPERTY_SETATTR_P1_KWARGS,
            item='qualifiers',
            new_value=None,
            exp_attrs=dict(
                qualifiers={},
            ),
        ),
        None, None, True
    ),
    (
        "Set existing qualifier to new CIMQualifier with correct name",
        dict(
            obj_kwargs=CIMPROPERTY_SETATTR_P1_KWARGS,
            item=('qualifiers', 'Q1'),
            new_value=CIMQualifier('Q1', True),
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set existing qualifier to new CIMQualifier with incorrect name",
        dict(
            obj_kwargs=CIMPROPERTY_SETATTR_P1_KWARGS,
            item=('qualifiers', 'Q1'),
            new_value=CIMQualifier('Q1x', True),
            # No checking for correct name since this sets an item in a normal
            # NocaseDict:
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1x', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new qualifier to new CIMQualifier with correct name",
        dict(
            obj_kwargs=CIMPROPERTY_SETATTR_P1_KWARGS,
            item=('qualifiers', 'Q2'),
            new_value=CIMQualifier('Q2', True),
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1', False),
                    Q2=CIMQualifier('Q2', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new qualifier to new CIMQualifier with incorrect name",
        dict(
            obj_kwargs=CIMPROPERTY_SETATTR_P1_KWARGS,
            item=('qualifiers', 'Q2'),
            new_value=CIMQualifier('Q2x', True),
            # No checking for correct name since this sets an item in a normal
            # NocaseDict:
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1', False),
                    Q2=CIMQualifier('Q2x', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new qualifier with name None",
        dict(
            obj_kwargs=CIMPROPERTY_SETATTR_P1_KWARGS,
            item=('qualifiers', None),
            # Name of CIMQualifier object is not None, to get over that check.
            new_value=CIMQualifier('Qnone', True),
            exp_attrs=None,
        ),
        ValueError if version_info >= (1, 0) else TypeError, None, True
        # None as key in NocaseDict
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMPROPERTY_SETATTR)
@simplified_test_function
@log_entry_exit
def test_CIMProperty_setattr(
        testcase, obj_kwargs, item, new_value, exp_attrs):
    """
    Test function for CIMProperty set attribute
    """

    obj = CIMProperty(**obj_kwargs)

    if isinstance(item, tuple):
        attr_name, attr_key = item
        attr_dict = getattr(obj, attr_name)

        # The code to be tested (for setting a single dict item)
        attr_dict[attr_key] = new_value

    else:
        attr_name = item

        # The code to be tested (for setting the entire attribute)
        setattr(obj, attr_name, new_value)

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

    for attr_name in exp_attrs:
        exp_value = exp_attrs[attr_name]
        value = getattr(obj, attr_name)
        assert value == exp_value


TESTCASES_CIMPROPERTY_HASH_EQ = [

    # Testcases for CIMProperty.__hash__() and __eq__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: CIMProperty object #1 to be tested.
    #   * obj2: CIMProperty object #2 to be tested.
    #   * exp_equal: Expected equality of the object hash values.
    # * 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

    # Name tests
    (
        "Name, equal with same lexical case",
        dict(
            obj1=CIMProperty('Prop1', value=''),
            obj2=CIMProperty('Prop1', value=''),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Name, equal with different lexical case",
        dict(
            obj1=CIMProperty('Prop1', value=''),
            obj2=CIMProperty('prOP1', value=''),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Name, different",
        dict(
            obj1=CIMProperty('Prop1', value=''),
            obj2=CIMProperty('Prop1_x', value=''),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Value tests
    (
        "Value, strings with different lexical case",
        dict(
            obj1=CIMProperty('Prop1', value='abc'),
            obj2=CIMProperty('Prop1', value='Abc'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Value, strings with None / string",
        dict(
            obj1=CIMProperty('Prop1', value=None, type='string'),
            obj2=CIMProperty('Prop1', value='abc', type='string'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Value, strings with None / empty string",
        dict(
            obj1=CIMProperty('Prop1', value=None, type='string'),
            obj2=CIMProperty('Prop1', value='', type='string'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Value, strings with string / None",
        dict(
            obj1=CIMProperty('Prop1', value='abc', type='string'),
            obj2=CIMProperty('Prop1', value=None, type='string'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Value, strings with empty string / None",
        dict(
            obj1=CIMProperty('Prop1', value='', type='string'),
            obj2=CIMProperty('Prop1', value=None, type='string'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Value, strings with None / None",
        dict(
            obj1=CIMProperty('Prop1', value=None, type='string'),
            obj2=CIMProperty('Prop1', value=None, type='string'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Value, different types. string with uint32",
        dict(
            obj1=CIMProperty('Prop1', value='abc', type='string'),
            obj2=CIMProperty('Prop1', value=Uint32(42), type='string'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Value, datetime with unequal datetime",
        dict(
            obj1=CIMProperty('Prop1', value=DATETIME1_OBJ, type='datetime'),
            obj2=CIMProperty('Prop1', value=DATETIME2_OBJ, type='datetime'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Value, datetime with equal datetime",
        dict(
            obj1=CIMProperty('Prop1', value=DATETIME1_OBJ, type='datetime'),
            obj2=CIMProperty('Prop1', value=DATETIME1_OBJ, type='datetime'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Value, datetime with uint8",
        dict(
            obj1=CIMProperty('Prop1', value=DATETIME1_OBJ, type='datetime'),
            obj2=CIMProperty('Prop1', value=42, type='uint8'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Value, uint8 with datetime",
        dict(
            obj1=CIMProperty('Prop1', value=42, type='uint8'),
            obj2=CIMProperty('Prop1', value=DATETIME1_OBJ, type='datetime'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Array value, same types uint8",
        dict(
            obj1=CIMProperty(
                'Prop1',
                value=[Uint8(x) for x in [1, 2, 3]],
                type='uint8'
            ),
            obj2=CIMProperty(
                'Prop1',
                value=[Uint8(x) for x in [1, 2, 3]],
                type='uint8'
            ),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Array value, different types uint8 and uint32",
        dict(
            obj1=CIMProperty(
                'Prop1',
                value=[Uint8(x) for x in [1, 2, 3]],
                type='uint8'
            ),
            obj2=CIMProperty(
                'Prop1',
                value=[Uint32(x) for x in [1, 2, 3]],
                type='uint32'
            ),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Type tests
    (
        "Type, different",
        dict(
            obj1=CIMProperty('Prop1', value=7, type='uint8'),
            obj2=CIMProperty('Prop1', value=7, type='sint8'),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Reference_class tests
    (
        "Reference class, equal with same lexical case",
        dict(
            obj1=CIMProperty('Prop1', value=None, type='reference',
                             reference_class='CIM_Ref'),
            obj2=CIMProperty('Prop1', value=None, type='reference',
                             reference_class='CIM_Ref'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Reference class, equal with different lexical case",
        dict(
            obj1=CIMProperty('Prop1', value=None, type='reference',
                             reference_class='CIM_Ref'),
            obj2=CIMProperty('Prop1', value=None, type='reference',
                             reference_class='Cim_ref'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Reference class, different",
        dict(
            obj1=CIMProperty('Prop1', value=None, type='reference',
                             reference_class='CIM_Ref'),
            obj2=CIMProperty('Prop1', value=None, type='reference',
                             reference_class='CIM_Ref_x'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Reference class, with equal CIMInstanceName",
        dict(
            obj1=CIMProperty('Prop1', value=CIMInstanceName('CIM_Ref'),
                             type='reference', reference_class='CIM_Ref'),
            obj2=CIMProperty('Prop1', value=CIMInstanceName('CIM_Ref'),
                             type='reference', reference_class='CIM_Ref'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Reference class, with equal CIMInstanceName",
        dict(
            obj1=CIMProperty('Prop1', value=CIMInstanceName('CIM_Ref'),
                             type='reference', reference_class='CIM_Ref'),
            obj2=CIMProperty('Prop1', value=None,
                             type='reference', reference_class='CIM_Ref'),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Embedded_object tests
    (
        "Embedded object, equal",
        dict(
            obj1=CIMProperty('Prop1', value=None, type='string',
                             embedded_object='instance'),
            obj2=CIMProperty('Prop1', value=None, type='string',
                             embedded_object='instance'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Embedded object, different",
        dict(
            obj1=CIMProperty('Prop1', value=None, type='string',
                             embedded_object='instance'),
            obj2=CIMProperty('Prop1', value=None, type='string',
                             embedded_object='object'),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Is_array tests
    (
        "Is_array, equal",
        dict(
            obj1=CIMProperty('Prop1', value=None, type='string',
                             is_array=True),
            obj2=CIMProperty('Prop1', value=None, type='string',
                             is_array=True),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Is_array, different",
        dict(
            obj1=CIMProperty('Prop1', value=None, type='string',
                             is_array=True),
            obj2=CIMProperty('Prop1', value=None, type='string',
                             is_array=False),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Array_size tests
    (
        "Array_size, equal",
        dict(
            obj1=CIMProperty('Prop1', value=None, type='string', is_array=True,
                             array_size=2),
            obj2=CIMProperty('Prop1', value=None, type='string', is_array=True,
                             array_size=2),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Array_size, different",
        dict(
            obj1=CIMProperty('Prop1', value=None, type='string', is_array=True,
                             array_size=2),
            obj2=CIMProperty('Prop1', value=None, type='string', is_array=True,
                             array_size=3),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Propagated tests
    (
        "Propagated, equal",
        dict(
            obj1=CIMProperty('Prop1', value=None, type='string',
                             propagated=True),
            obj2=CIMProperty('Prop1', value=None, type='string',
                             propagated=True),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Propagated, different",
        dict(
            obj1=CIMProperty('Prop1', value=None, type='string',
                             propagated=True),
            obj2=CIMProperty('Prop1', value=None, type='string',
                             propagated=False),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Class_origin tests
    (
        "Class_origin, equal with same lexical case",
        dict(
            obj1=CIMProperty('Prop1', value=None, type='string',
                             propagated=True, class_origin='CIM_Org'),
            obj2=CIMProperty('Prop1', value=None, type='string',
                             propagated=True, class_origin='CIM_Org'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Class_origin, equal with different lexical case",
        dict(
            obj1=CIMProperty('Prop1', value=None, type='string',
                             propagated=True, class_origin='CIM_Org'),
            obj2=CIMProperty('Prop1', value=None, type='string',
                             propagated=True, class_origin='Cim_org'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Class_origin, different",
        dict(
            obj1=CIMProperty('Prop1', value=None, type='string',
                             propagated=True, class_origin='CIM_Org'),
            obj2=CIMProperty('Prop1', value=None, type='string',
                             propagated=True, class_origin='CIM_Org_x'),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Qualifiers tests
    (
        "Matching qualifiers, qualifier names with same lexical case",
        dict(
            obj1=CIMProperty('Prop1', value='',
                             qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMProperty('Prop1', value='',
                             qualifiers={'Cheepy': 'Birds'}),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Matching qualifiers, qualifier names with different lexical case",
        dict(
            obj1=CIMProperty('Prop1', value='',
                             qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMProperty('Prop1', value='',
                             qualifiers={'cheepy': 'Birds'}),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Non-matching qualifiers, one qualifier more",
        dict(
            obj1=CIMProperty('Prop1', value=''),
            obj2=CIMProperty('Prop1', value='',
                             qualifiers={'Cheepy': 'Birds'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching qualifiers, one qualifier less",
        dict(
            obj1=CIMProperty('Prop1', value='',
                             qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMProperty('Prop1', value=''),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching qualifiers, different qualifiers",
        dict(
            obj1=CIMProperty('Prop1', value='',
                             qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMProperty('Prop1', value='',
                             qualifiers={'Creepy': 'Ants'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Matching qualifiers, with values that differ in lexical case",
        dict(
            obj1=CIMProperty('Prop1', value='',
                             qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMProperty('Prop1', value='',
                             qualifiers={'Cheepy': 'birds'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Matching qualifiers, with values that are unicode / string",
        dict(
            obj1=CIMProperty('Prop1', value='',
                             qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMProperty('Prop1', value='',
                             qualifiers={'Cheepy': 'Birds'}),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Equal qualifiers with a number of types",
        dict(
            obj1=CIMProperty(
                'Prop1', value='',
                qualifiers={
                    'Name': 'Foo',
                    'Boolean': False,
                    'Number': Uint8(42),
                }
            ),
            obj2=CIMProperty(
                'Prop1', value='',
                qualifiers={
                    'Name': 'Foo',
                    'Boolean': False,
                    'Number': Uint8(42),
                }
            ),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Qualifier with different types: bool True / string 'TRUE'",
        dict(
            obj1=CIMProperty('Prop1', value='',
                             qualifiers={'Foo': True}),
            obj2=CIMProperty('Prop1', value='',
                             qualifiers={'Foo': 'TRUE'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Qualifier with different types: bool False / string 'FALSE'",
        dict(
            obj1=CIMProperty('Prop1', value='',
                             qualifiers={'Foo': False}),
            obj2=CIMProperty('Prop1', value='',
                             qualifiers={'Foo': 'FALSE'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Matching qualifiers, but with different order",
        dict(
            obj1=CIMProperty(
                'Prop1',
                value='abc',
                qualifiers=[
                    CIMQualifier('Q1', 'Birds'),
                    CIMQualifier('Q2', 'Ants'),
                ]
            ),
            obj2=CIMProperty(
                'Prop1',
                value='abc',
                qualifiers=[
                    CIMQualifier('Q2', 'Ants'),
                    CIMQualifier('Q1', 'Birds'),
                ]
            ),
            exp_equal=True,
        ),
        None, None, True
    ),
]

TESTCASES_CIMPROPERTY_EQ = [

    # Additional testcases for CIMProperty.__eq__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: CIMProperty object #1 to be tested.
    #   * obj2: CIMProperty object #2 to be tested.
    #   * exp_equal: Expected equality of the object hash values.
    # * 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

    # Name tests
    (
        "Invalid type of second object: string",
        dict(
            obj1=CIMProperty('CIM_Foo', 'abc'),
            obj2='abc',
            exp_equal=None,
        ),
        TypeError, None, True
    ),
    (
        "Invalid type of second object: string",
        dict(
            obj1=CIMProperty('CIM_Foo', Uint8(42)),
            obj2=42,
            exp_equal=None,
        ),
        TypeError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMPROPERTY_HASH_EQ)
@simplified_test_function
@log_entry_exit
def test_CIMProperty_hash(testcase, obj1, obj2, exp_equal):
    """
    Test function for CIMProperty.__hash__().
    """

    # The code to be tested
    hash1 = hash(obj1)
    hash2 = hash(obj2)

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

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

    if exp_equal:
        assert hash1 == hash2
    else:
        assert hash1 != hash2


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMPROPERTY_HASH_EQ + TESTCASES_CIMPROPERTY_EQ)
@simplified_test_function
@log_entry_exit
def test_CIMProperty_eq(testcase, obj1, obj2, exp_equal):
    """
    Test function for CIMProperty.__eq__().
    """

    # The code to be tested
    equal = (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

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

    assert equal == exp_equal


TESTCASES_CIMPROPERTY_STR_REPR = [

    # Testcases for CIMProperty.__repr__(), __str__() / repr(), str()

    # Each list item is a testcase tuple with these items:
    # * obj: CIMProperty object to be tested.

    (
        CIMProperty(
            name='Spotty',
            value='Foot')
    ),
    (
        CIMProperty(
            name='Spotty',
            value='Foot',
            type='string')
    ),
    (
        CIMProperty(
            name='Spotty',
            value=None,
            type='reference',
            reference_class='CIM_Foo')
    ),
    (
        CIMProperty(
            name='Spotty',
            value=CIMInstance('CIM_Bar'),
            type='string',
            embedded_object='instance')
    ),
    (
        CIMProperty(
            name='Spotty',
            value=CIMInstance('CIM_Bar'),
            type='string',
            embedded_object='object')
    ),
    (
        CIMProperty(
            name='Spotty',
            value=CIMClass('CIM_Bar'),
            type='string',
            embedded_object='object')
    ),
    (
        CIMProperty(
            name='Spotty',
            value=['Foot'],
            is_array=True)
    ),
]


@pytest.mark.parametrize(
    "obj",
    TESTCASES_CIMPROPERTY_STR_REPR)
@log_entry_exit
def test_CIMProperty_str(obj):
    """
    Test function for CIMProperty.__str__() / str()
    """

    # The code to be tested
    s = str(obj)

    assert re.match(r'^CIMProperty\(', s)

    exp_name = _format('name={0!A}', obj.name)
    assert exp_name in s

    exp_value = _format('value={0!A}', obj.value)
    assert exp_value in s

    exp_type = _format('type={0!A}', obj.type)
    assert exp_type in s

    exp_reference_class = _format('reference_class={0!A}', obj.reference_class)
    assert exp_reference_class in s

    exp_embedded_object = _format('embedded_object={0!A}', obj.embedded_object)
    assert exp_embedded_object in s

    exp_is_array = _format('is_array={0!A}', obj.is_array)
    assert exp_is_array in s


@pytest.mark.parametrize(
    "obj",
    TESTCASES_CIMPROPERTY_STR_REPR)
@log_entry_exit
def test_CIMProperty_repr(obj):
    """
    Test function for CIMProperty.__repr__() / repr()
    """

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

    assert re.match(r'^CIMProperty\(', r)

    exp_name = _format('name={0!A}', obj.name)
    assert exp_name in r

    exp_value = _format('value={0!A}', obj.value)
    assert exp_value in r

    exp_type = _format('type={0!A}', obj.type)
    assert exp_type in r

    exp_reference_class = _format('reference_class={0!A}', obj.reference_class)
    assert exp_reference_class in r

    exp_embedded_object = _format('embedded_object={0!A}', obj.embedded_object)
    assert exp_embedded_object in r

    exp_is_array = _format('is_array={0!A}', obj.is_array)
    assert exp_is_array in r

    exp_array_size = _format('array_size={0!A}', obj.array_size)
    assert exp_array_size in r

    exp_class_origin = _format('class_origin={0!A}', obj.class_origin)
    assert exp_class_origin in r

    exp_propagated = _format('propagated={0!A}', obj.propagated)
    assert exp_propagated in r

    exp_qualifiers = _format('qualifiers={0!A}', obj.qualifiers)
    assert exp_qualifiers in r


TESTCASES_CIMPROPERTY_TOCIMXML = [

    # Testcases for CIMProperty.tocimxml() and tocimxmlstr()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: CIMProperty object to be tested.
    #   * kwargs: Dict of input args for tocimxml() (empty).
    #   * exp_xml_str: Expected CIM-XML string, as a tuple/list of parts.
    # * 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

    # Tests with name variations
    (
        "Name with ASCII characters, as byte string",
        dict(
            obj=CIMProperty(b'Foo', value=None, type='string'),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Name with ASCII characters, as unicode string",
        dict(
            obj=CIMProperty('Foo', value=None, type='string'),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Name with non-ASCII UCS-2 characters, as byte string",
        dict(
            obj=CIMProperty(b'Foo\xC3\xA9', value=None, type='string'),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo\u00E9" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Name with non-ASCII UCS-2 characters, as unicode string",
        dict(
            obj=CIMProperty('Foo\u00E9', value=None, type='string'),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo\u00E9" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Name with non-UCS-2 characters, as byte string",
        dict(
            obj=CIMProperty(b'Foo\xF0\x90\x85\x82', value=None, type='string'),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo\U00010142" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Name with non-UCS-2 characters, as unicode string",
        dict(
            obj=CIMProperty('Foo\U00010142', value=None, type='string'),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo\U00010142" TYPE="string"/>',
            )
        ),
        None, None, True
    ),

    # Tests with qualifier variations
    (
        "Two qualifiers and string value",
        dict(
            obj=CIMProperty(
                'Foo', type='string', value='foo',
                qualifiers=[
                    CIMQualifier('Q2', 'bla'),
                    CIMQualifier('Q1', True),
                ],
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="string">',
                '<QUALIFIER NAME="Q2" TYPE="string">',
                '<VALUE>bla</VALUE>',
                '</QUALIFIER>',
                '<QUALIFIER NAME="Q1" TYPE="boolean">',
                '<VALUE>TRUE</VALUE>',
                '</QUALIFIER>',
                '<VALUE>foo</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),

    # Scalar properties with boolean type
    (
        "Scalar property with boolean type, value NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='boolean', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with boolean type, value True",
        dict(
            obj=CIMProperty(
                'Foo', type='boolean', value=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="boolean">',
                '<VALUE>TRUE</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with boolean type, value False",
        dict(
            obj=CIMProperty(
                'Foo', type='boolean', value=False,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="boolean">',
                '<VALUE>FALSE</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),

    # Scalar properties with string type
    (
        "Scalar property with string type, value NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='string', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with string type, value has one entry with ASCII "
        "characters",
        dict(
            obj=CIMProperty(
                'Foo', type='string', value='foo',
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="string">',
                '<VALUE>foo</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with string type, value has one entry with non-ASCII "
        "UCS-2 characters",
        dict(
            obj=CIMProperty(
                'Foo', type='string', value='foo\u00E9',
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="string">',
                '<VALUE>foo\u00E9</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with string type, value has one entry with non-UCS-2 "
        "characters",
        dict(
            obj=CIMProperty(
                'Foo', type='string', value='foo\U00010142',
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="string">',
                '<VALUE>foo\U00010142</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),

    # Scalar properties with embedded objects/instances
    (
        "Scalar property with embedded instance type containing an instance",
        dict(
            obj=CIMProperty(
                'Foo', type='string',
                value=CIMInstance('CIM_Emb'), embedded_object='instance',
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY EmbeddedObject="instance"',
                ' NAME="Foo" TYPE="string">',
                '<VALUE>',
                '&lt;INSTANCE CLASSNAME=&quot;CIM_Emb&quot;/&gt;',
                '</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with embedded object type containing an instance",
        dict(
            obj=CIMProperty(
                'Foo', type='string',
                value=CIMInstance('CIM_Emb'), embedded_object='object',
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY EmbeddedObject="object"',
                ' NAME="Foo" TYPE="string">',
                '<VALUE>',
                '&lt;INSTANCE CLASSNAME=&quot;CIM_Emb&quot;/&gt;',
                '</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with embedded object type containing a class",
        dict(
            obj=CIMProperty(
                'Foo', type='string',
                value=CIMClass('CIM_Emb'), embedded_object='object',
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY EmbeddedObject="object"',
                ' NAME="Foo" TYPE="string">',
                '<VALUE>',
                '&lt;CLASS NAME=&quot;CIM_Emb&quot;/&gt;',
                '</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),

    # Scalar properties with char16 type
    (
        "Scalar property with char16 type, value NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='char16', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="char16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with char16 type, value has one entry with an ASCII "
        "character",
        dict(
            obj=CIMProperty(
                'Foo', type='char16', value='f',
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="char16">',
                '<VALUE>f</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with char16 type, value has one entry with a "
        "non-ASCII UCS-2 character",
        dict(
            obj=CIMProperty(
                'Foo', type='char16', value='\u00E9',
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="char16">',
                '<VALUE>\u00E9</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with char16 type, value has one entry with a "
        "non-UCS-2 character (invalid as per DSP0004, but tolerated by pywbem)",
        dict(
            obj=CIMProperty(
                'Foo', type='char16', value='\U00010142',
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="char16">',
                '<VALUE>\U00010142</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),

    # Scalar properties with uint8 type
    (
        "Scalar property with uint8 type, value NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='uint8', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="uint8"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with uint8 type, value has one entry in range",
        dict(
            obj=CIMProperty(
                'Foo', type='uint8', value=42,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="uint8">',
                '<VALUE>42</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),

    # Scalar properties with uint16 type
    (
        "Scalar property with uint16 type, value NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='uint16', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="uint16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with uint16 type, value has one entry in range",
        dict(
            obj=CIMProperty(
                'Foo', type='uint16', value=1234,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="uint16">',
                '<VALUE>1234</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),

    # Scalar properties with uint32 type
    (
        "Scalar property with uint32 type, value NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='uint32', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="uint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with uint32 type, value has one entry in range",
        dict(
            obj=CIMProperty(
                'Foo', type='uint32', value=12345678,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="uint32">',
                '<VALUE>12345678</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),

    # Scalar properties with uint64 type
    (
        "Scalar property with uint64 type, value NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='uint64', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="uint64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with uint64 type, value has one entry in range",
        dict(
            obj=CIMProperty(
                'Foo', type='uint64', value=123456789012,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="uint64">',
                '<VALUE>123456789012</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),

    # Scalar properties with sint8 type
    (
        "Scalar property with sint8 type, value NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='sint8', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="sint8"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with sint8 type, value has one entry in range",
        dict(
            obj=CIMProperty(
                'Foo', type='sint8', value=-42,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="sint8">',
                '<VALUE>-42</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),

    # Scalar properties with sint16 type
    (
        "Scalar property with sint16 type, value NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='sint16', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="sint16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with sint16 type, value has one entry in range",
        dict(
            obj=CIMProperty(
                'Foo', type='sint16', value=-1234,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="sint16">',
                '<VALUE>-1234</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),

    # Scalar properties with sint32 type
    (
        "Scalar property with sint32 type, value NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='sint32', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="sint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with sint32 type, value has one entry in range",
        dict(
            obj=CIMProperty(
                'Foo', type='sint32', value=-12345678,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="sint32">',
                '<VALUE>-12345678</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),

    # Scalar properties with sint64 type
    (
        "Scalar property with sint64 type, value NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='sint64', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="sint64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with sint64 type, value has one entry in range",
        dict(
            obj=CIMProperty(
                'Foo', type='sint64', value=-123456789012,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="sint64">',
                '<VALUE>-123456789012</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),

    # Scalar properties with real32 type
    (
        "Scalar property with real32 type, value NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="real32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with real32 type, value between 0 and 1",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=0.42,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="real32">',
                '<VALUE>0.42</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with real32 type, value with max number of "
        "significant digits (11)",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=1.2345678901,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="real32">',
                '<VALUE>1.2345678901</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with real32 type, value larger 1 without exponent",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=42.0,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="real32">',
                '<VALUE>42.0</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with real32 type, value with small negative exponent",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=-42.0E-3,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="real32">',
                '<VALUE>-0.042</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with real32 type, value with small positive exponent",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=-42.0E+3,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="real32">',
                '<VALUE>-42000.0</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with real32 type, value with large negative exponent",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=-42.0E-30,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="real32">',
                '<VALUE>-4.2E-29</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with real32 type, value with large positive exponent",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=-42.0E+30,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="real32">',
                '<VALUE>-4.2E+31</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with real32 type, special value INF",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=float('inf'),
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="real32">',
                '<VALUE>INF</VALUE>',  # must be upper case
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with real32 type, special value -INF",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=float('-inf'),
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="real32">',
                '<VALUE>-INF</VALUE>',  # must be upper case
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with real32 type, special value NaN",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=float('nan'),
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="real32">',
                '<VALUE>NaN</VALUE>',  # must be upper case
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),

    # Scalar properties with real64 type
    (
        "Scalar property with real64 type, value NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="real64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with real64 type, value between 0 and 1",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=0.42,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="real64">',
                '<VALUE>0.42</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, False  # py27: 0.41999999999999998
    ),
    (
        "Scalar property with real64 type, value with max number of "
        "significant digits (17)",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=1.2345678901234567,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="real64">',
                '<VALUE>1.2345678901234567</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with real64 type, value larger 1 without exponent",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=42.0,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="real64">',
                '<VALUE>42.0</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with real64 type, value with small negative exponent",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=-42.0E-3,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="real64">',
                '<VALUE>-0.042</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, False  # py27: -0.042000000000000003
    ),
    (
        "Scalar property with real64 type, value with small positive exponent",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=-42.0E+3,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="real64">',
                '<VALUE>-42000.0</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with real64 type, value with large negative exponent",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=-42.0E-30,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="real64">',
                '<VALUE>-4.2E-29</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, False  # py27: -4.1999999999999998E-29
    ),
    (
        "Scalar property with real64 type, value with large positive exponent",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=-42.0E+30,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="real64">',
                '<VALUE>-4.2E+31</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, False  # py27: -4.1999999999999996E+31
    ),
    (
        "Scalar property with real64 type, special value INF",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=float('inf'),
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="real64">',
                '<VALUE>INF</VALUE>',  # must be upper case
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with real64 type, special value -INF",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=float('-inf'),
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="real64">',
                '<VALUE>-INF</VALUE>',  # must be upper case
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with real64 type, special value NaN",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=float('nan'),
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="real64">',
                '<VALUE>NaN</VALUE>',  # must be upper case
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),

    # Scalar properties with datetime type
    (
        "Scalar property with datetime type, value NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='datetime', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="datetime"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with datetime type, point in time value",
        dict(
            obj=CIMProperty(
                'Foo', type='datetime',
                value=datetime(2014, 9, 22, 10, 49, 20, 524789),
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="datetime">',
                '<VALUE>20140922104920.524789+000</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with datetime type, interval value",
        dict(
            obj=CIMProperty(
                'Foo', type='datetime',
                value=timedelta(10, 49, 20),
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" TYPE="datetime">',
                '<VALUE>00000010000049.000020:000</VALUE>',
                '</PROPERTY>',
            )
        ),
        None, None, True
    ),

    # Scalar properties with reference type
    (
        "Scalar property with reference type, value NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='reference', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.REFERENCE NAME="Foo"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar property with reference type, classname-only ref value",
        dict(
            obj=CIMProperty(
                'Foo', type='reference',
                value=CIMInstanceName('CIM_Foo'),
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.REFERENCE NAME="Foo">',
                '<VALUE.REFERENCE>',
                '<INSTANCENAME CLASSNAME="CIM_Foo"/>',
                '</VALUE.REFERENCE>',
                '</PROPERTY.REFERENCE>',
            )
        ),
        None, MissingKeybindingsWarning, True
    ),

    # Array properties with boolean type
    (
        "Array property with boolean type, value is NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='boolean', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with boolean type, value is empty array",
        dict(
            obj=CIMProperty(
                'Foo', type='boolean', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="boolean">',
                '<VALUE.ARRAY/>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with boolean type, value has one entry NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='boolean', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="boolean">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with boolean type, value has one entry True",
        dict(
            obj=CIMProperty(
                'Foo', type='boolean', value=[True],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="boolean">',
                '<VALUE.ARRAY>',
                '<VALUE>TRUE</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with boolean type, value has one entry False",
        dict(
            obj=CIMProperty(
                'Foo', type='boolean', value=[False],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="boolean">',
                '<VALUE.ARRAY>',
                '<VALUE>FALSE</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),

    # Array properties with string type
    (
        "Array property with string type, value is NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='string', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with string type, value is empty array",
        dict(
            obj=CIMProperty(
                'Foo', type='string', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="string">',
                '<VALUE.ARRAY/>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with string type, value has one entry NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='string', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="string">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with string type, value has one entry with ASCII "
        "characters",
        dict(
            obj=CIMProperty(
                'Foo', type='string', value=['foo'],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="string">',
                '<VALUE.ARRAY>',
                '<VALUE>foo</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with string type, value has one entry with non-ASCII "
        "UCS-2 chars",
        dict(
            obj=CIMProperty(
                'Foo', type='string', value=['foo\u00E9'],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="string">',
                '<VALUE.ARRAY>',
                '<VALUE>foo\u00E9</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with string type, value has one entry with non-UCS-2 "
        "characters",
        dict(
            obj=CIMProperty(
                'Foo', type='string', value=['foo\U00010142'],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="string">',
                '<VALUE.ARRAY>',
                '<VALUE>foo\U00010142</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),

    # Array properties with embedded objects/instances
    (
        "Array property with embedded instance type, value has one entry that "
        "is an instance",
        dict(
            obj=CIMProperty(
                'Foo', type='string', is_array=True,
                value=[CIMInstance('CIM_Emb')], embedded_object='instance',
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY EmbeddedObject="instance"',
                ' NAME="Foo" TYPE="string">',
                '<VALUE.ARRAY>',
                '<VALUE>',
                '&lt;INSTANCE CLASSNAME=&quot;CIM_Emb&quot;/&gt;',
                '</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with embedded object type, value has one entry that "
        "is an instance",
        dict(
            obj=CIMProperty(
                'Foo', type='string', is_array=True,
                value=[CIMInstance('CIM_Emb')], embedded_object='object',
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY EmbeddedObject="object"',
                ' NAME="Foo" TYPE="string">',
                '<VALUE.ARRAY>',
                '<VALUE>',
                '&lt;INSTANCE CLASSNAME=&quot;CIM_Emb&quot;/&gt;',
                '</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with embedded object type, value has one entry that "
        "is a class",
        dict(
            obj=CIMProperty(
                'Foo', type='string', is_array=True,
                value=[CIMClass('CIM_Emb')], embedded_object='object',
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY EmbeddedObject="object"',
                ' NAME="Foo" TYPE="string">',
                '<VALUE.ARRAY>',
                '<VALUE>',
                '&lt;CLASS NAME=&quot;CIM_Emb&quot;/&gt;',
                '</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),

    # Array properties with char16 type
    (
        "Array property with char16 type, value is NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='char16', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="char16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with char16 type, value is empty array",
        dict(
            obj=CIMProperty(
                'Foo', type='char16', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="char16">',
                '<VALUE.ARRAY/>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with char16 type, value has one entry NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='char16', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="char16">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with char16 type, value has one entry with an ASCII "
        "character",
        dict(
            obj=CIMProperty(
                'Foo', type='char16', value=['f'],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="char16">',
                '<VALUE.ARRAY>',
                '<VALUE>f</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with char16 type, value has one entry with a "
        "non-ASCII UCS-2 character",
        dict(
            obj=CIMProperty(
                'Foo', type='char16', value=['\u00E9'],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="char16">',
                '<VALUE.ARRAY>',
                '<VALUE>\u00E9</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with char16 type, value has one entry with a "
        "non-UCS-2 character (invalid as per DSP0004, but tolerated by pywbem)",
        dict(
            obj=CIMProperty(
                'Foo', type='char16', value=['\U00010142'],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="char16">',
                '<VALUE.ARRAY>',
                '<VALUE>\U00010142</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),

    # Array properties with uint8 type
    (
        "Array property with uint8 type, value is NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='uint8', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="uint8"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with uint8 type, value is empty array",
        dict(
            obj=CIMProperty(
                'Foo', type='uint8', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="uint8">',
                '<VALUE.ARRAY/>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with uint8 type, value has one entry NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='uint8', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="uint8">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with uint8 type, value has one entry in range",
        dict(
            obj=CIMProperty(
                'Foo', type='uint8', value=[42],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="uint8">',
                '<VALUE.ARRAY>',
                '<VALUE>42</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),

    # Array properties with uint16 type
    (
        "Array property with uint16 type, value is NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='uint16', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="uint16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with uint16 type, value is empty array",
        dict(
            obj=CIMProperty(
                'Foo', type='uint16', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="uint16">',
                '<VALUE.ARRAY/>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with uint16 type, value has one entry NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='uint16', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="uint16">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with uint16 type, value has one entry in range",
        dict(
            obj=CIMProperty(
                'Foo', type='uint16', value=[1234],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="uint16">',
                '<VALUE.ARRAY>',
                '<VALUE>1234</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),

    # Array properties with uint32 type
    (
        "Array property with uint32 type, value is NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='uint32', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="uint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with uint32 type, value is empty array",
        dict(
            obj=CIMProperty(
                'Foo', type='uint32', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="uint32">',
                '<VALUE.ARRAY/>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with uint32 type, value has one entry NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='uint32', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="uint32">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with uint32 type, value has one entry in range",
        dict(
            obj=CIMProperty(
                'Foo', type='uint32', value=[12345678],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="uint32">',
                '<VALUE.ARRAY>',
                '<VALUE>12345678</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),

    # Array properties with uint64 type
    (
        "Array property with uint64 type, value is NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='uint64', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="uint64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with uint64 type, value is empty array",
        dict(
            obj=CIMProperty(
                'Foo', type='uint64', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="uint64">',
                '<VALUE.ARRAY/>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with uint64 type, value has one entry NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='uint64', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="uint64">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with uint64 type, value has one entry in range",
        dict(
            obj=CIMProperty(
                'Foo', type='uint64', value=[123456789012],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="uint64">',
                '<VALUE.ARRAY>',
                '<VALUE>123456789012</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),

    # Array properties with sint8 type
    (
        "Array property with sint8 type, value is NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='sint8', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="sint8"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with sint8 type, value is empty array",
        dict(
            obj=CIMProperty(
                'Foo', type='sint8', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="sint8">',
                '<VALUE.ARRAY/>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with sint8 type, value has one entry NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='sint8', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="sint8">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with sint8 type, value has one entry in range",
        dict(
            obj=CIMProperty(
                'Foo', type='sint8', value=[-42],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="sint8">',
                '<VALUE.ARRAY>',
                '<VALUE>-42</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),

    # Array properties with sint16 type
    (
        "Array property with sint16 type, value is NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='sint16', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="sint16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with sint16 type, value is empty array",
        dict(
            obj=CIMProperty(
                'Foo', type='sint16', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="sint16">',
                '<VALUE.ARRAY/>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with sint16 type, value has one entry NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='sint16', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="sint16">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with sint16 type, value has one entry in range",
        dict(
            obj=CIMProperty(
                'Foo', type='sint16', value=[-1234],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="sint16">',
                '<VALUE.ARRAY>',
                '<VALUE>-1234</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),

    # Array properties with sint32 type
    (
        "Array property with sint32 type, value is NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='sint32', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="sint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with sint32 type, value is empty array",
        dict(
            obj=CIMProperty(
                'Foo', type='sint32', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="sint32">',
                '<VALUE.ARRAY/>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with sint32 type, value has one entry NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='sint32', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="sint32">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with sint32 type, value has one entry in range",
        dict(
            obj=CIMProperty(
                'Foo', type='sint32', value=[-12345678],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="sint32">',
                '<VALUE.ARRAY>',
                '<VALUE>-12345678</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),

    # Array properties with sint64 type
    (
        "Array property with sint64 type, value is NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='sint64', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="sint64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with sint64 type, value is empty array",
        dict(
            obj=CIMProperty(
                'Foo', type='sint64', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="sint64">',
                '<VALUE.ARRAY/>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with sint64 type, value has one entry NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='sint64', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="sint64">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with sint64 type, value has one entry in range",
        dict(
            obj=CIMProperty(
                'Foo', type='sint64', value=[-123456789012],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="sint64">',
                '<VALUE.ARRAY>',
                '<VALUE>-123456789012</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),

    # Array properties with real32 type
    (
        "Array property with real32 type, value is NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with real32 type, value is empty array",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY/>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with real32 type, value has one entry NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with real32 type, value has one entry between 0 and 1",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=[0.42],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>0.42</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with real32 type, value has one entry with max number "
        "of significant digits (11)",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=[1.2345678901],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>1.2345678901</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with real32 type, value has one entry larger 1 "
        "without exponent",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=[42.0],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>42.0</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with real32 type, value has one entry with small "
        "negative exponent",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=[-42.0E-3],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>-0.042</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with real32 type, value has one entry with small "
        "positive exponent",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=[-42.0E+3],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>-42000.0</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with real32 type, value has one entry with large "
        "negative exponent",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=[-42.0E-30],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>-4.2E-29</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with real32 type, value has one entry with large "
        "positive exponent",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=[-42.0E+30],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>-4.2E+31</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with real32 type, value has one entry that is special "
        "value INF",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=[float('inf')],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>INF</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with real32 type, value has one entry that is special "
        "value -INF",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=[float('-inf')],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>-INF</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with real32 type, value has one entry that is special "
        "value NaN",
        dict(
            obj=CIMProperty(
                'Foo', type='real32', value=[float('nan')],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>NaN</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),

    # Array properties with real64 type
    (
        "Array property with real64 type, value is NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with real64 type, value is empty array",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY/>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with real64 type, value has one entry NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with real64 type, value has one entry between 0 and 1",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=[0.42],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>0.42</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, False  # py27: 0.41999999999999998
    ),
    (
        "Array property with real64 type, value has one entry with max number "
        "of significant digits (17)",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=[1.2345678901234567],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>1.2345678901234567</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with real64 type, value has one entry larger 1 "
        "without exponent",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=[42.0],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>42.0</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with real64 type, value has one entry with small "
        "negative exponent",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=[-42.0E-3],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>-0.042</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, False  # py27: -0.042000000000000003
    ),
    (
        "Array property with real64 type, value has one entry with small "
        "positive exponent",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=[-42.0E+3],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>-42000.0</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with real64 type, value has one entry with large "
        "negative exponent",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=[-42.0E-30],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>-4.2E-29</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, False  # py27: -4.1999999999999998E-29
    ),
    (
        "Array property with real64 type, value has one entry with large "
        "positive exponent",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=[-42.0E+30],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>-4.2E+31</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, False  # py27: -4.1999999999999996E+31
    ),
    (
        "Array property with real64 type, value has one entry that is special "
        "value INF",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=[float('inf')],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>INF</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with real64 type, value has one entry that is special "
        "value -INF",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=[float('-inf')],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>-INF</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with real64 type, value has one entry that is special "
        "value NaN",
        dict(
            obj=CIMProperty(
                'Foo', type='real64', value=[float('nan')],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>NaN</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),

    # Array properties with datetime type
    (
        "Array property with datetime type, value is NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='datetime', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="datetime"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with datetime type, value is empty array",
        dict(
            obj=CIMProperty(
                'Foo', type='datetime', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="datetime">',
                '<VALUE.ARRAY/>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with datetime type, value has one entry NULL",
        dict(
            obj=CIMProperty(
                'Foo', type='datetime', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="datetime">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with datetime type, value has one entry point in time",
        dict(
            obj=CIMProperty(
                'Foo', type='datetime',
                value=[datetime(2014, 9, 22, 10, 49, 20, 524789)],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="datetime">',
                '<VALUE.ARRAY>',
                '<VALUE>20140922104920.524789+000</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),
    (
        "Array property with datetime type, value has one entry interval",
        dict(
            obj=CIMProperty(
                'Foo', type='datetime',
                value=[timedelta(10, 49, 20)],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY.ARRAY NAME="Foo" TYPE="datetime">',
                '<VALUE.ARRAY>',
                '<VALUE>00000010000049.000020:000</VALUE>',
                '</VALUE.ARRAY>',
                '</PROPERTY.ARRAY>',
            )
        ),
        None, None, True
    ),

    # Array properties with reference type are not allowed

    # Tests with class_origin, propagated variations
    (
        "Class origin set",
        dict(
            obj=CIMProperty(
                'Foo', type='uint32', value=None, class_origin='CIM_Origin',
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY CLASSORIGIN="CIM_Origin" NAME="Foo" TYPE="uint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Propagated set to True",
        dict(
            obj=CIMProperty(
                'Foo', type='uint32', value=None, propagated=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" PROPAGATED="true" TYPE="uint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Propagated set to False",
        dict(
            obj=CIMProperty(
                'Foo', type='uint32', value=None, propagated=False,
            ),
            kwargs={},
            exp_xml_str=(
                '<PROPERTY NAME="Foo" PROPAGATED="false" TYPE="uint32"/>',
            )
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMPROPERTY_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMProperty_tocimxml(testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMProperty.tocimxml().
    """

    # The code to be tested
    obj_xml = obj.tocimxml(**kwargs)

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

    obj_xml_str = obj_xml.toxml()
    exp_xml_str = ''.join(exp_xml_str)
    validate_cim_xml_obj(obj, obj_xml_str, exp_xml_str)


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMPROPERTY_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMProperty_tocimxmlstr(testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMProperty.tocimxmlstr().
    """

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(**kwargs)

    # 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(obj_xml_str, str)

    exp_xml_str = ''.join(exp_xml_str)
    validate_cim_xml_obj(obj, obj_xml_str, exp_xml_str)


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMPROPERTY_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMProperty_tocimxmlstr_indent_int(
        testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMProperty.tocimxmlstr() with indent as integer.
    """

    indent = 4
    indent_str = ' ' * indent

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(indent=indent, **kwargs)

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

    obj_xml = obj.tocimxml(**kwargs)  # This is tested elsewhere
    exp_xml_str = obj_xml.toprettyxml(indent=indent_str)

    assert obj_xml_str == exp_xml_str, (
        f"{obj.__class__.__name__}.tocimxmlstr(indent={indent!r}) "
        "returns unexpected CIM-XML string")


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMPROPERTY_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMProperty_tocimxmlstr_indent_str(
        testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMProperty.tocimxmlstr() with indent as string.
    """

    indent = 4
    indent_str = ' ' * indent

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(indent=indent_str, **kwargs)

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

    obj_xml = obj.tocimxml(**kwargs)  # This is tested elsewhere
    exp_xml_str = obj_xml.toprettyxml(indent=indent_str)

    assert obj_xml_str == exp_xml_str, (
        f"{obj.__class__.__name__}.tocimxmlstr(indent={indent_str!r}) "
        "returns unexpected CIM-XML string")


TESTCASES_CIMPROPERTY_TOMOF = [

    # Testcases for CIMProperty.tomof()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: CIMProperty object to be tested.
    #   * kwargs: Dict of input args to tomof() method
    #   * exp_mof: Expected MOF result string, or None if failed.
    # * 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

    (
        "class property, all components",
        dict(
            obj=CIMProperty(
                name='P1',
                value="abc",
                type='string',
                qualifiers=[
                    CIMQualifier('Q1', value='abc', type='string'),
                    CIMQualifier('Q2', value=Uint32(42), type='uint32'),
                ],
            ),
            kwargs=dict(
                is_instance=False,
                indent=12,
            ),
            exp_mof="""\
               [Q1 ( "abc" ),
                Q2 ( 42 )]
            string P1 = "abc";\n""",
        ),
        None, None, True
    ),
    (
        "class property, no qualifiers",
        dict(
            obj=CIMProperty(
                name='P1',
                value=None,
                type='string',
            ),
            kwargs=dict(
                is_instance=False,
                indent=12,
            ),
            exp_mof="""\
            string P1;\n""",
        ),
        None, None, True
    ),
    (
        "class property, one scalar single line qualifier",
        dict(
            obj=CIMProperty(
                name='P1',
                value=None,
                type='string',
                qualifiers=[
                    CIMQualifier('Q1', value='abc', type='string'),
                ],
            ),
            kwargs=dict(
                is_instance=False,
                indent=12,
            ),
            exp_mof="""\
               [Q1 ( "abc" )]
            string P1;\n""",
        ),
        None, None, True
    ),
    (
        "class property, one scalar multi line qualifier",
        dict(
            obj=CIMProperty(
                name='P1',
                value=None,
                type='string',
                qualifiers=[
                    CIMQualifier('Q1', value=('abc def ' * 10 + 'z'),
                                 type='string'),
                ],
            ),
            kwargs=dict(
                is_instance=False,
                indent=12,
            ),
            exp_mof="""\
               [Q1 (
                   "abc def abc def abc def abc def abc def abc def abc def "
                   "abc def abc def abc def z" )]
            string P1;\n""",
        ),
        None, None, True
    ),
    (
        "class property, two scalar single line qualifiers",
        dict(
            obj=CIMProperty(
                name='P1',
                value=None,
                type='string',
                qualifiers=[
                    CIMQualifier('Q1', value='abc', type='string'),
                    CIMQualifier('Q2', value=Uint32(42), type='uint32'),
                ],
            ),
            kwargs=dict(
                is_instance=False,
                indent=12,
            ),
            exp_mof="""\
               [Q1 ( "abc" ),
                Q2 ( 42 )]
            string P1;\n""",
        ),
        None, None, True
    ),
    (
        "class property, two scalar multi line qualifiers",
        dict(
            obj=CIMProperty(
                name='P1',
                value=None,
                type='string',
                qualifiers=[
                    CIMQualifier('Q1', value=('abc def ' * 10 + 'z'),
                                 type='string'),
                    CIMQualifier('Q2', value=('rst uvw ' * 10 + 'z'),
                                 type='string'),
                ],
            ),
            kwargs=dict(
                is_instance=False,
                indent=12,
            ),
            exp_mof="""\
               [Q1 (
                   "abc def abc def abc def abc def abc def abc def abc def "
                   "abc def abc def abc def z" ),
                Q2 (
                   "rst uvw rst uvw rst uvw rst uvw rst uvw rst uvw rst uvw "
                   "rst uvw rst uvw rst uvw z" )]
            string P1;\n""",
        ),
        None, None, True
    ),
    (
        "class property, one array single line qualifier",
        dict(
            obj=CIMProperty(
                name='P1',
                value=None,
                type='string',
                qualifiers=[
                    CIMQualifier('Q1', value=['abc', 'def'], type='string'),
                ],
            ),
            kwargs=dict(
                is_instance=False,
                indent=12,
            ),
            exp_mof="""\
               [Q1 { "abc", "def" }]
            string P1;\n""",
        ),
        None, None, True
    ),
    (
        "class property, one array multi line qualifier with short items",
        dict(
            obj=CIMProperty(
                name='P1',
                value=None,
                type='string',
                qualifiers=[
                    CIMQualifier(
                        'Q1',
                        value=[f'abcdef{_i:02d}' for _i in range(0, 10)],
                        type='string'),
                ],
            ),
            kwargs=dict(
                is_instance=False,
                indent=12,
            ),
            exp_mof="""\
               [Q1 { "abcdef00", "abcdef01", "abcdef02", "abcdef03",
                   "abcdef04", "abcdef05", "abcdef06", "abcdef07",
                   "abcdef08", "abcdef09" }]
            string P1;\n""",
        ),
        None, None, True
    ),
    (
        "class property, one array multi line qualifier with long items",
        dict(
            obj=CIMProperty(
                name='P1',
                value=None,
                type='string',
                qualifiers=[
                    CIMQualifier(
                        'Q1',
                        value=['abc def ' * 10 + f'z{_i:02d}'
                               for _i in range(0, 2)],
                        type='string'),
                ],
            ),
            kwargs=dict(
                is_instance=False,
                indent=12,
            ),
            exp_mof="""\
               [Q1 {
                   "abc def abc def abc def abc def abc def abc def abc def "
                   "abc def abc def abc def z00",
                   "abc def abc def abc def abc def abc def abc def abc def "
                   "abc def abc def abc def z01" }]
            string P1;\n""",
        ),
        None, None, True
    ),
    (
        "class property, type string, no default value",
        dict(
            obj=CIMProperty(
                name='P1',
                value=None,
                type='string',
            ),
            kwargs=dict(
                is_instance=False,
                indent=12,
            ),
            exp_mof="""\
            string P1;\n""",
        ),
        None, None, True
    ),
    (
        "class property, type string, with default value",
        dict(
            obj=CIMProperty(
                name='P1',
                value="abc",
                type='string',
            ),
            kwargs=dict(
                is_instance=False,
                indent=12,
            ),
            exp_mof="""\
            string P1 = "abc";\n""",
        ),
        None, None, True
    ),
    (
        "class property, type char16, no default value",
        dict(
            obj=CIMProperty(
                name='P1',
                value=None,
                type='char16',
            ),
            kwargs=dict(
                is_instance=False,
                indent=12,
            ),
            exp_mof="""\
            char16 P1;\n""",
        ),
        None, None, True
    ),
    (
        "class property, type char16, with default value",
        dict(
            obj=CIMProperty(
                name='P1',
                value="a",
                type='char16',
            ),
            kwargs=dict(
                is_instance=False,
                indent=12,
            ),
            exp_mof="""\
            char16 P1 = 'a';\n""",  # bug fixed in 0.12
        ),
        None, None, True
    ),
    (
        "class property, variable size array of uint32, no default value",
        dict(
            obj=CIMProperty(
                name='P1',
                value=None,
                type='uint32',
                is_array=True,
            ),
            kwargs=dict(
                is_instance=False,
                indent=12,
            ),
            exp_mof="""\
            uint32 P1[];\n""",
        ),
        None, None, True
    ),
    (
        "class property, variable size array of uint32, with default value",
        dict(
            obj=CIMProperty(
                name='P1',
                value=[1, 2, 3],
                type='uint32',
                is_array=True,
            ),
            kwargs=dict(
                is_instance=False,
                indent=12,
            ),
            exp_mof="""\
            uint32 P1[] = { 1, 2, 3 };\n""",
        ),
        None, None, True
    ),
    (
        "class property, fixed size array of sint64, no default value",
        dict(
            obj=CIMProperty(
                name='P1',
                value=None,
                type='sint64',
                is_array=True,
                array_size=5,
            ),
            kwargs=dict(
                is_instance=False,
                indent=12,
            ),
            exp_mof="""\
            sint64 P1[5];\n""",
        ),
        None, None, True
    ),
    (
        "class property, type reference, no default value",
        dict(
            obj=CIMProperty(
                name='P1',
                value=None,
                type='reference',
                reference_class="RC",
            ),
            kwargs=dict(
                is_instance=False,
                indent=12,
            ),
            exp_mof="""\
            RC REF P1;\n""",
        ),
        None, None, True
    ),
    (
        "class property, type reference, with default value",
        dict(
            obj=CIMProperty(
                name='P1',
                value=CIMInstanceName("RC", dict(k1='abc')),
                type='reference',
                reference_class="RC",
            ),
            kwargs=dict(
                is_instance=False,
                indent=12,
            ),
            exp_mof="""\
            RC REF P1 = "/:RC.k1=\\"abc\\"";\n""",    # bug fixed in 0.12
        ),
        None, None, True
    ),
    (
        "instance property, all components",
        dict(
            obj=CIMProperty(
                name='P1',
                value=["abc", "def"],
                type='string',
                is_array=True,
                array_size=5,
                qualifiers=[
                    CIMQualifier('Q1', value='abc', type='string'),
                    CIMQualifier('Q2', value=Uint32(42), type='uint32'),
                ],
            ),
            kwargs=dict(
                is_instance=True,
                indent=12,
            ),
            exp_mof="""\
            P1 = { "abc", "def" };\n""",  # bug: '= =', fixed in 0.12
        ),
        None, None, True
    ),
    (
        "instance string property, with NULL value",
        dict(
            obj=CIMProperty(
                name='P1',
                value=None,
                type='string',
            ),
            kwargs=dict(
                is_instance=True,
                indent=12,
            ),
            exp_mof="""\
            P1 = NULL;\n""",
        ),
        None, None, True
    ),

    # pylint: disable=line-too-long

    (
        "instance string property, with multi-line scalar value",
        dict(
            obj=CIMProperty(
                name='P1',
                value=('abc def ' * 10 + 'z'),
                type='string',
            ),
            kwargs=dict(
                is_instance=True,
                indent=12,
            ),
            exp_mof="""\
            P1 =
               "abc def abc def abc def abc def abc def abc def abc def abc "
               "def abc def abc def z";\n""",
        ),
        None, None, True
    ),

    # pylint: enable=line-too-long

    # pylint: disable=line-too-long

    (
        "instance string array property, with multi-line short items",
        dict(
            obj=CIMProperty(
                name='P1',
                value=[f'abcdef{_i:02d}' for _i in range(0, 10)],
                type='string',
            ),
            kwargs=dict(
                is_instance=True,
                indent=12,
            ),
            exp_mof="""\
            P1 = { "abcdef00", "abcdef01", "abcdef02", "abcdef03", "abcdef04",
               "abcdef05", "abcdef06", "abcdef07", "abcdef08", "abcdef09" };\n""",  # noqa: E501
            # bug: '= =', fixed in 0.12
        ),
        None, None, True
    ),

    # pylint: enable=line-too-long

    (
        "instance uint32 property",
        dict(
            obj=CIMProperty(
                name='P1',
                value=42,
                type='uint32',
            ),
            kwargs=dict(
                is_instance=True,
                indent=12,
            ),
            exp_mof="""\
            P1 = 42;\n""",
        ),
        None, None, True
    ),
    (
        "instance uint32 array property, empty array",
        dict(
            obj=CIMProperty(
                name='P1',
                value=[],
                type='uint32',
                is_array=True,
            ),
            kwargs=dict(
                is_instance=True,
                indent=12,
            ),
            exp_mof="""\
            P1 = { };\n""",
        ),
        None, None, True
    ),
    (
        "instance uint32 array property, single-line",
        dict(
            obj=CIMProperty(
                name='P1',
                value=list(range(0, 10)),
                type='uint32',
                is_array=True,
            ),
            kwargs=dict(
                is_instance=True,
                indent=12,
            ),
            exp_mof="""\
            P1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };\n""",  # bug fixed in 0.12
        ),
        None, None, True
    ),
    (
        "instance uint32 array property, multi-line",
        dict(
            obj=CIMProperty(
                name='P1',
                value=list(range(0, 20)),
                type='uint32',
                is_array=True,
            ),
            kwargs=dict(
                is_instance=True,
                indent=12,
            ),
            exp_mof="""\
            P1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
               17, 18, 19 };\n""",  # bug fixed in 0.12
        ),
        None, None, True
    ),
    (
        "Instance property with name that does not fit onto line by 10",
        dict(
            obj=CIMProperty('Very_long_property_name', value='abc'),
            kwargs=dict(
                is_instance=True,
                indent=3,
                maxline=18,
            ),
            exp_mof='   Very_long_property_name =\n      "abc";\n',
        ),
        None, None, True
    ),
    (
        "uint64 instance property with value that does not fit onto line by 1",
        dict(
            obj=CIMProperty('p1', value=Uint64(1998012513304050)),
            kwargs=dict(
                is_instance=True,
                indent=3,
                maxline=22,
            ),
            exp_mof=None,
        ),
        ValueError, None, True
    ),
    (
        "uint64 instance property with value that exactly fits onto line",
        dict(
            obj=CIMProperty('p1', value=Uint64(1998012513304050)),
            kwargs=dict(
                is_instance=True,
                indent=3,
                maxline=23,
            ),
            exp_mof=''
            '   p1 =\n'
            '      1998012513304050;\n',
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMPROPERTY_TOMOF)
@simplified_test_function
@log_entry_exit
def test_CIMProperty_tomof(testcase, obj, kwargs, exp_mof):
    """
    Test function for CIMProperty.tomof().
    """

    # The code to be tested
    mof = obj.tomof(**kwargs)

    # 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(mof, str)
    assert mof == exp_mof


@log_entry_exit
def test_CIMProperty_tomof_special1():
    """
    Special test CIMProperty.tomof(): Datetime value with string type
    """

    datetime_dt = datetime(2018, 1, 5, 15, 0, 0, 0)
    datetime_obj = CIMDateTime(datetime_dt)

    obj = CIMProperty(name='P1', value=datetime_obj, type='datetime')

    # Set an inconsistent CIM type. This is not prevented at this point.
    obj.type = 'string'
    assert obj.type == 'string'

    with pytest.raises(TypeError):

        # The code to be tested
        obj.tomof(is_instance=False)


@log_entry_exit
def test_CIMProperty_tomof_special2():
    """
    Special test CIMProperty.tomof(): Plain float value
    """

    obj = CIMProperty(name='P1', value=42.1, type='real32')
    assert isinstance(obj.value, Real32)

    # Set the value back to a plain float.
    obj.value = 42.1
    assert isinstance(obj.value, float)

    exp_mof = "real32 P1 = 42.1;\n"

    # The code to be tested
    mof = obj.tomof(is_instance=False)

    assert mof == exp_mof


TESTCASES_CIMQUALIFIER_INIT = [

    # Testcases for CIMQualifier.__init__()

    # 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 CIMQualifier().
    #   * init_kwargs: Dict of keyword arguments to CIMQualifier().
    #   * exp_attrs: Dict of expected attributes of resulting 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

    # Order of positional arguments
    (
        "Verify order of positional arguments",
        dict(
            init_args=[
                'FooQual',
                'abc',
                'string',
                False,
                True,
                True,
                False,
                True,
            ],
            init_kwargs={},
            exp_attrs=dict(
                name='FooQual',
                value='abc',
                type='string',
                propagated=False,
                overridable=True,
                tosubclass=True,
                toinstance=False,
                translatable=True,
            )
        ),
        None, None, True
    ),

    # Name tests
    (
        "Verify that bytes name is converted to unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                name=b'FooParam', value='abc'
            ),
            exp_attrs=dict(
                name='FooParam', value='abc', type=exp_type_string
            )
        ),
        None, None, True
    ),
    (
        "Verify that unicode name remains unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam', value='abc'
            ),
            exp_attrs=dict(
                name='FooParam', value='abc', type=exp_type_string
            )
        ),
        None, None, True
    ),

    # Value/type tests
    (
        "Verify that reference type fails",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooQual', type='reference', value=None),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that bytes value is converted to unicode and "
        "type is implied to string",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam', value=b'abc'
            ),
            exp_attrs=dict(
                name='FooParam', value='abc',
                type=exp_type_string
            )
        ),
        None, None, True
    ),
    (
        "Verify that unicode value remains unicode and "
        "type is implied to string",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam', value='abc'
            ),
            exp_attrs=dict(
                name='FooParam', value='abc', type=exp_type_string
            )
        ),
        None, None, True
    ),
    (
        "Verify uint8 value without type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam', value=Uint8(42)
            ),
            exp_attrs=dict(
                name='FooParam', value=Uint8(42), type=exp_type_uint8
            )
        ),
        None, None, True
    ),
    (
        "Verify uint8 value with type",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam', value=Uint8(42), type='uint8'
            ),
            exp_attrs=dict(
                name='FooParam', value=Uint8(42), type='uint8'
            )
        ),
        None, None, True
    ),
    (
        "Verify float value with type real64",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam', value=42.0, type='real64'
            ),
            exp_attrs=dict(
                name='FooParam', value=Real64(42.0), type='real64'
            )
        ),
        None, None, True
    ),
    (
        "Verify float value with type real32",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam', value=42.0, type='real32'
            ),
            exp_attrs=dict(
                name='FooParam', value=Real32(42.0), type='real32'
            )
        ),
        None, None, True
    ),
    (
        "Verify that boolean value True remains boolean and "
        "type is implied to boolean",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam', value=True
            ),
            exp_attrs=dict(
                name='FooParam', value=True, type=exp_type_boolean
            )
        ),
        None, None, True
    ),
    (
        "Verify that boolean value False remains boolean and "
        "type is implied to boolean",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam', value=False
            ),
            exp_attrs=dict(
                name='FooParam', value=False, type=exp_type_boolean
            )
        ),
        None, None, True
    ),
    (
        "Verify that bytes array value is converted to unicode and "
        "type is implied to string",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam', value=[b'abc']
            ),
            exp_attrs=dict(
                name='FooParam', value=['abc'],
                type=exp_type_string
            )
        ),
        None, None, True
    ),
    (
        "Verify that unicode array value remains unicode and "
        "type is implied to string",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam', value=['abc']
            ),
            exp_attrs=dict(
                name='FooParam', value=['abc'], type=exp_type_string
            )
        ),
        None, None, True
    ),
    (
        "Verify that boolean array value [False] remains boolean and "
        "type is implied to boolean",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam', value=[False]
            ),
            exp_attrs=dict(
                name='FooParam', value=[False], type=exp_type_boolean
            )
        ),
        None, None, True
    ),
    (
        "Verify that boolean array value [True] remains boolean and "
        "type is implied to boolean",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam', value=[True]
            ),
            exp_attrs=dict(
                name='FooParam', value=[True], type=exp_type_boolean
            )
        ),
        None, None, True
    ),
    (
        "Verify that setting boolean properties to 42 results in True "
        "(since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam', value='abc',
                propagated=42, overridable=42, tosubclass=42,
                toinstance=42, translatable=42
            ),
            exp_attrs=dict(
                name='FooParam', value='abc', type=exp_type_string,
                propagated=True, overridable=True, tosubclass=True,
                toinstance=True, translatable=True
            )
        ),
        None, None, True
    ),
    (
        "Verify that setting boolean properties to 'false' results in True "
        "(using Python bool rules, since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam', value='abc',
                propagated='false', overridable='false', tosubclass='false',
                toinstance='false', translatable='false'
            ),
            exp_attrs=dict(
                name='FooParam', value='abc', type=exp_type_string,
                propagated=True, overridable=True, tosubclass=True,
                toinstance=True, translatable=True
            )
        ),
        None, None, True
    ),
    (
        "Verify that setting boolean properties to 0 results in False "
        "(since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam', value='abc',
                propagated=0, overridable=0, tosubclass=0,
                toinstance=0, translatable=0
            ),
            exp_attrs=dict(
                name='FooParam', value='abc', type=exp_type_string,
                propagated=False, overridable=False, tosubclass=False,
                toinstance=False, translatable=False
            )
        ),
        None, None, True
    ),

    # Check documented examples
    (
        "Verify documented example 1",
        dict(
            init_args=[],
            init_kwargs=dict(name='MyString', value='abc'),
            exp_attrs=dict(name='MyString', value='abc', type=exp_type_string)
        ),
        None, None, True
    ),
    (
        "Verify documented example 2 (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name='MyNum', value=42, type='uint8'),
            exp_attrs=dict(name='MyNum', value=Uint8(42), type=exp_type_uint8)
        ),
        None, None, True
    ),
    (
        "Verify documented example 3",
        dict(
            init_args=[],
            init_kwargs=dict(name='MyNum', value=Uint8(42)),
            exp_attrs=dict(name='MyNum', value=Uint8(42), type=exp_type_uint8)
        ),
        None, None, True
    ),
    (
        "Verify documented example 4",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='MyNumArray', value=[1, 2, 3], type='uint8'
            ),
            exp_attrs=dict(
                name='MyNumArray', value=[Uint8(1), Uint8(2), Uint8(3)],
                type='uint8'
            )
        ),
        None, None, True
    ),
    (
        "Verify documented example 5",
        dict(
            init_args=[],
            init_kwargs=dict(name='MyString', value=None, type='string'),
            exp_attrs=dict(name='MyString', value=None, type='string')
        ),
        None, None, True
    ),
    (
        "Verify documented example 6",
        dict(
            init_args=[],
            init_kwargs=dict(name='MyNum', value=None, type='uint8'),
            exp_attrs=dict(name='MyNum', value=None, type='uint8')
        ),
        None, None, True
    ),

    # Exception testcases
    (
        "Verify that name None fails (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name=None, value='abc'),
            exp_attrs=None
        ),
        ValueError, None, True  # property name None
    ),
    (
        "Verify that value None without type fails "
        "(raises ValueError instead of TypeError since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooQual', value=None),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that an invalid type fails (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooQual', value=None, type='xxx'),
            exp_attrs=None
        ),
        ValueError, None, True  # invalid type
    ),
    (
        "Verify that value [None] without type fails "
        "(raises ValueError instead of TypeError since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooQual', value=[None]),
            exp_attrs=None
        ),
        ValueError, None, True  # cannot infer
    ),
    (
        "Verify that value [] without type fails "
        "(raises ValueError instead of TypeError since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooQual', value=[]),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that value of int without type fails "
        "(raises ValueError instead of TypeError since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooQual', value=42),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that value of float without type fails",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooQual', value=42.1),
            exp_attrs=None
        ),
        # raises ValueError instead of TypeError since 0.12
        ValueError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMQUALIFIER_INIT)
@simplified_test_function
@log_entry_exit
def test_CIMQualifier_init(testcase, init_args, init_kwargs, exp_attrs):
    """
    Test function for CIMQualifier.__init__()
    """

    # The code to be tested
    obj = CIMQualifier(*init_args, **init_kwargs)

    assert not hasattr(obj, '__dict__')

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

    exp_name = exp_attrs['name']
    assert obj.name == exp_name
    assert isinstance(obj.name, type(exp_name))

    exp_value = exp_attrs['value']
    assert obj.value == exp_value
    assert isinstance(obj.value, type(exp_value))

    exp_type = exp_attrs['type']
    assert obj.type == exp_type
    assert isinstance(obj.type, type(exp_type))

    exp_propagated = exp_attrs.get('propagated', None)
    assert obj.propagated == exp_propagated
    assert isinstance(obj.propagated, type(exp_propagated))

    exp_overridable = exp_attrs.get('overridable', None)
    assert obj.overridable == exp_overridable
    assert isinstance(obj.overridable, type(exp_overridable))

    exp_tosubclass = exp_attrs.get('tosubclass', None)
    assert obj.tosubclass == exp_tosubclass
    assert isinstance(obj.tosubclass, type(exp_tosubclass))

    exp_toinstance = exp_attrs.get('toinstance', None)
    assert obj.toinstance == exp_toinstance
    assert isinstance(obj.toinstance, type(exp_toinstance))

    exp_translatable = exp_attrs.get('translatable', None)
    assert obj.translatable == exp_translatable
    assert isinstance(obj.translatable, type(exp_translatable))


TESTCASES_CIMQUALIFIER_COPY = [

    # Testcases for CIMQualifier.copy()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj_kwargs: Dict of init kwargs for original CIMQualifier.
    # * 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

    (
        "Qualifier of type string, with all boolean attributes set",
        dict(
            obj_kwargs=dict(
                name='QualFoo',
                value='Bar',
                type='string',
                propagated=True,
                overridable=True,
                tosubclass=True,
                toinstance=True,
                translatable=True,
            )
        ),
        None, None, True
    ),
    (
        "Qualifier of type datetime, with all boolean attributes None",
        dict(
            obj_kwargs=dict(
                name='QualFoo',
                value='20191005155152.123456-120',
                type='datetime',
                propagated=False,
                overridable=False,
                tosubclass=False,
                toinstance=False,
                translatable=False,
            )
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMQUALIFIER_COPY)
@simplified_test_function
@log_entry_exit
def test_CIMQualifier_copy(testcase, obj_kwargs):
    """
    Test function for CIMQualifier.copy()
    """

    obj1 = CIMQualifier(**obj_kwargs)

    # The code to be tested
    obj2 = obj1.copy()

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

    # Verify that the copy is equal to the original (performs deep equality)
    assert obj2 == obj1

    # Verify that the copy is a different object
    assert id(obj2) != id(obj1)

    # TODO: Decide whether mutable child objects should be copied, and add test:
    # # Verify that the mutable child objects are different objects
    #     if not isinstance(obj1.value, (int, str)):
    #         assert id(obj2.value) != id(obj1.value)

    # Verify that the copy can be modified and the original remains unchanged.
    # Most of the attribute setters don't validate the change, because multiple
    # such changes might be needed to make the object consistent again.

    obj1_name = obj1.name
    obj2.name = 'SomeNewQual'
    assert obj1.name == obj1_name

    # TODO: Decide whether value objects should be copied, and add test:
    # obj1_value = obj1.value
    # obj2.value = None  # avoid going through the types
    # assert obj1.value == obj1_value

    obj1_type = obj1.type
    obj2.type = 'uint8' if obj1.type == 'string' else 'string'
    assert obj1.type == obj1_type

    obj1_propagated = obj1.propagated
    obj2.propagated = not obj1.propagated
    assert obj1.propagated == obj1_propagated

    obj1_overridable = obj1.overridable
    obj2.overridable = not obj1.overridable
    assert obj1.overridable == obj1_overridable

    obj1_tosubclass = obj1.tosubclass
    obj2.tosubclass = not obj1.tosubclass
    assert obj1.tosubclass == obj1_tosubclass

    obj1_toinstance = obj1.toinstance
    obj2.toinstance = not obj1.toinstance
    assert obj1.toinstance == obj1_toinstance

    obj1_translatable = obj1.translatable
    obj2.translatable = not obj1.translatable
    assert obj1.translatable == obj1_translatable


CIMQUALIFIER_SETATTR_Q1_KWARGS = dict(
    name='Q1',
    value='V1',
    type='string',
    propagated=None,
    overridable=None,
    tosubclass=None,
    toinstance=None,
    translatable=None,
)

TESTCASES_CIMQUALIFIER_SETATTR = [

    # Testcases for CIMQualifier set attribute

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj_kwargs: Dict of init kwargs for CIMQualifier.
    #   * item: Name of CIMQualifier attr to set, or tuple
    #     (item, key) to set a single item in an attr that is a dict.
    #   * new_value: New value to set.
    #   * exp_attrs: Dict of expected attributes, for validation.
    # * 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

    # Tests that set the name attribute
    (
        "Set name to different string",
        dict(
            obj_kwargs=CIMQUALIFIER_SETATTR_Q1_KWARGS,
            item='name',
            new_value='Q2',
            exp_attrs=dict(
                name='Q2',
            ),
        ),
        None, None, True
    ),
    (
        "Set name to None",
        # Before 0.12.0, the implementation allowed the name to be None,
        # although the documentation required it not to be None.
        # We test the implemented behavior. Since 0.12.0, this raises
        # ValueError.
        dict(
            obj_kwargs=CIMQUALIFIER_SETATTR_Q1_KWARGS,
            item='name',
            new_value=None,
            exp_attrs=dict(
                name=None,
            ),
        ),
        # raises ValueError since 0.12
        ValueError, None, True
    ),

    # Tests that set the value attribute for string types
    (
        "For string type, set value to 7-bit ASCII unicode string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
            ),
            item='value',
            new_value='V2',
            exp_attrs=dict(
                value='V2',
            ),
        ),
        None, None, True
    ),
    (
        "For string type, set value to 7-bit ASCII byte string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
            ),
            item='value',
            new_value=b'V2',
            exp_attrs=dict(
                value='V2',
            ),
        ),
        None, None, True
    ),
    (
        "For string type, set value to non-UCS-2 unicode string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
            ),
            item='value',
            new_value='Foo\U00010142',
            exp_attrs=dict(
                value='Foo\U00010142',
            ),
        ),
        None, None, True
    ),
    (
        "For string type, set value to non-UCS-2 UTF-8 byte string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
            ),
            item='value',
            new_value=b'Foo\xF0\x90\x85\x82',
            exp_attrs=dict(
                value='Foo\U00010142',
            ),
        ),
        None, None, True
    ),
    (
        "For string type, set value to None",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for char16 types
    (
        "For char16 type, set value to 7-bit ASCII Char16 string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value=Char16('U'),
            exp_attrs=dict(
                value='U',
            ),
        ),
        None, None, True
    ),
    (
        "For char16 type, set value to 7-bit ASCII unicode string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value='U',
            exp_attrs=dict(
                value='U',
            ),
        ),
        None, None, True
    ),
    (
        "For char16 type, set value to 7-bit ASCII byte string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value=b'U',
            exp_attrs=dict(
                value='U',
            ),
        ),
        None, None, True
    ),
    (
        "For char16 type, set value to non-UCS-2 Char16 string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value=Char16('\U00010142'),
            exp_attrs=dict(
                value='\U00010142',
            ),
        ),
        None, None, True
    ),
    (
        "For char16 type, set value to non-UCS-2 unicode string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value='\U00010142',
            exp_attrs=dict(
                value='\U00010142',
            ),
        ),
        None, None, True
    ),
    (
        "For char16 type, set value to non-UCS-2 UTF-8 byte string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value=b'\xF0\x90\x85\x82',
            exp_attrs=dict(
                value='\U00010142',
            ),
        ),
        None, None, True
    ),
    (
        "For char16 type, set value to None",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for boolean type
    (
        "For boolean type, set value to boolean True",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=False,
                type='boolean',
            ),
            item='value',
            new_value=True,
            exp_attrs=dict(
                value=True,
            ),
        ),
        None, None, True
    ),
    (
        "For boolean type, set value to boolean False",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=True,
                type='boolean',
            ),
            item='value',
            new_value=False,
            exp_attrs=dict(
                value=False,
            ),
        ),
        None, None, True
    ),
    (
        "For boolean type, set value to string 'true'",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=False,
                type='boolean',
            ),
            item='value',
            new_value='true',
            exp_attrs=dict(
                value=True,
            ),
        ),
        None, None, True
    ),
    (
        "For boolean type, set value to string 'false'",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=False,
                type='boolean',
            ),
            item='value',
            new_value='false',
            exp_attrs=dict(
                value=True,  # no processing of 'true'/'false' strings
            ),
        ),
        None, None, True
    ),
    (
        "For boolean type, set value to None",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=False,
                type='boolean',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for integer types
    (
        "For uint8 type, set value to integer out of range",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=42,
                type='uint8',
            ),
            item='value',
            new_value=256,
            exp_attrs=dict(
                value=None,
            ),
        ),
        ValueError, None, True  # out of range
    ),
    (
        "For uint64 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=42,
                type='uint64',
            ),
            item='value',
            new_value=12345678901234,
            exp_attrs=dict(
                value=12345678901234,
            ),
        ),
        None, None, True
    ),
    (
        "For sint8 type, set value to integer out of range",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=42,
                type='sint8',
            ),
            item='value',
            new_value=-129,
            exp_attrs=dict(
                value=None,
            ),
        ),
        ValueError, None, True  # out of range
    ),
    (
        "For sint64 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=42,
                type='sint64',
            ),
            item='value',
            new_value=-12345678901234,
            exp_attrs=dict(
                value=-12345678901234,
            ),
        ),
        None, None, True
    ),
    (
        "For sint32 type, set value to None",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=42,
                type='sint32',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for real types
    (
        "For real32 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=42.1,
                type='real32',
            ),
            item='value',
            new_value=-12345678890.1,
            exp_attrs=dict(
                value=-12345678890.1,
            ),
        ),
        None, None, True
    ),
    (
        "For real64 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=42.1,
                type='real64',
            ),
            item='value',
            new_value=-12345678890.1,
            exp_attrs=dict(
                value=-12345678890.1,
            ),
        ),
        None, None, True
    ),
    (
        "For real64 type, set value to None",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=42,
                type='real64',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for datetime type
    (
        "For datetime type, set value to datetime object",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value=datetime(
                year=2019, month=10, day=20, hour=15, minute=30, second=40,
                microsecond=654321, tzinfo=MinutesFromUTC(120)
            ),
            exp_attrs=dict(
                value=CIMDateTime(datetime(
                    year=2019, month=10, day=20, hour=15, minute=30, second=40,
                    microsecond=654321, tzinfo=MinutesFromUTC(120)
                )),
            ),
        ),
        None, None, True
    ),
    (
        "For datetime type, set value to point in time string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value='20191020153040.654321+120',
            exp_attrs=dict(
                value=CIMDateTime('20191020153040.654321+120'),
            ),
        ),
        None, None, True
    ),
    (
        "For datetime type, set value to point in time CIMDateTime",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value=CIMDateTime('20191020153040.654321+120'),
            exp_attrs=dict(
                value=CIMDateTime('20191020153040.654321+120'),
            ),
        ),
        None, None, True
    ),
    (
        "For datetime type, set value to timedelta object",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value=timedelta(25, (14 * 60 + 24) * 60 + 41, 234567),
            exp_attrs=dict(
                value=CIMDateTime(
                    timedelta(25, (14 * 60 + 24) * 60 + 41, 234567)
                ),
            ),
        ),
        None, None, True
    ),
    (
        "For datetime type, set value to interval string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value='00000173232441.234567:000',
            exp_attrs=dict(
                value=CIMDateTime('00000173232441.234567:000'),
            ),
        ),
        None, None, True
    ),
    (
        "For datetime type, set value to interval CIMDateTime",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value=CIMDateTime('00000173232441.234567:000'),
            exp_attrs=dict(
                value=CIMDateTime('00000173232441.234567:000'),
            ),
        ),
        None, None, True
    ),
    (
        "For datetime type, set value to None",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the type attribute
    (
        "For string type, set type to uint8",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
            ),
            item='type',
            new_value='uint8',
            exp_attrs=dict(
                type='uint8',
                value='V1',
            ),
        ),
        None, None, True
    ),
    (
        "For string type, set type to invalid type",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
            ),
            item='type',
            new_value='xxx',
            exp_attrs=None,
        ),
        ValueError, None, True  # invalid type
    ),
    (
        "For string type, set type to None",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
            ),
            item='type',
            new_value=None,
            exp_attrs=None,
        ),
        ValueError, None, True  # invalid type
    ),

    # Tests that set the propagated attribute
    (
        "Set propagated from False to True",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                propagated=False,
            ),
            item='propagated',
            new_value=True,
            exp_attrs=dict(
                propagated=True,
            ),
        ),
        None, None, True
    ),
    (
        "Set propagated from True to False",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                propagated=True,
            ),
            item='propagated',
            new_value=False,
            exp_attrs=dict(
                propagated=False,
            ),
        ),
        None, None, True
    ),
    (
        "Set propagated from False to 'false' (boolean conversion to True)",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                propagated=False,
            ),
            item='propagated',
            new_value='false',
            exp_attrs=dict(
                propagated=True,
            ),
        ),
        None, None, True
    ),
    (
        "Set propagated from False to 'true' (boolean conversion to True)",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                propagated=False,
            ),
            item='propagated',
            new_value='true',
            exp_attrs=dict(
                propagated=True,
            ),
        ),
        None, None, True
    ),

    # Tests that set the overridable attribute
    (
        "Set overridable from False to True",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                overridable=False,
            ),
            item='overridable',
            new_value=True,
            exp_attrs=dict(
                overridable=True,
            ),
        ),
        None, None, True
    ),
    (
        "Set overridable from True to False",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                overridable=True,
            ),
            item='overridable',
            new_value=False,
            exp_attrs=dict(
                overridable=False,
            ),
        ),
        None, None, True
    ),
    (
        "Set overridable from False to 'false' (boolean conversion to True)",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                overridable=False,
            ),
            item='overridable',
            new_value='false',
            exp_attrs=dict(
                overridable=True,
            ),
        ),
        None, None, True
    ),
    (
        "Set overridable from False to 'true' (boolean conversion to True)",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                overridable=False,
            ),
            item='overridable',
            new_value='true',
            exp_attrs=dict(
                overridable=True,
            ),
        ),
        None, None, True
    ),

    # Tests that set the tosubclass attribute
    (
        "Set tosubclass from False to True",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                tosubclass=False,
            ),
            item='tosubclass',
            new_value=True,
            exp_attrs=dict(
                tosubclass=True,
            ),
        ),
        None, None, True
    ),
    (
        "Set tosubclass from True to False",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                tosubclass=True,
            ),
            item='tosubclass',
            new_value=False,
            exp_attrs=dict(
                tosubclass=False,
            ),
        ),
        None, None, True
    ),
    (
        "Set tosubclass from False to 'false' (boolean conversion to True)",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                tosubclass=False,
            ),
            item='tosubclass',
            new_value='false',
            exp_attrs=dict(
                tosubclass=True,
            ),
        ),
        None, None, True
    ),
    (
        "Set tosubclass from False to 'true' (boolean conversion to True)",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                tosubclass=False,
            ),
            item='tosubclass',
            new_value='true',
            exp_attrs=dict(
                tosubclass=True,
            ),
        ),
        None, None, True
    ),

    # Tests that set the toinstance attribute
    (
        "Set toinstance from False to True",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                toinstance=False,
            ),
            item='toinstance',
            new_value=True,
            exp_attrs=dict(
                toinstance=True,
            ),
        ),
        None, None, True
    ),
    (
        "Set toinstance from True to False",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                toinstance=True,
            ),
            item='toinstance',
            new_value=False,
            exp_attrs=dict(
                toinstance=False,
            ),
        ),
        None, None, True
    ),
    (
        "Set toinstance from False to 'false' (boolean conversion to True)",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                toinstance=False,
            ),
            item='toinstance',
            new_value='false',
            exp_attrs=dict(
                toinstance=True,
            ),
        ),
        None, None, True
    ),
    (
        "Set toinstance from False to 'true' (boolean conversion to True)",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                toinstance=False,
            ),
            item='toinstance',
            new_value='true',
            exp_attrs=dict(
                toinstance=True,
            ),
        ),
        None, None, True
    ),

    # Tests that set the translatable attribute
    (
        "Set translatable from False to True",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                translatable=False,
            ),
            item='translatable',
            new_value=True,
            exp_attrs=dict(
                translatable=True,
            ),
        ),
        None, None, True
    ),
    (
        "Set translatable from True to False",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                translatable=True,
            ),
            item='translatable',
            new_value=False,
            exp_attrs=dict(
                translatable=False,
            ),
        ),
        None, None, True
    ),
    (
        "Set translatable from False to 'false' (boolean conversion to True)",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                translatable=False,
            ),
            item='translatable',
            new_value='false',
            exp_attrs=dict(
                translatable=True,
            ),
        ),
        None, None, True
    ),
    (
        "Set translatable from False to 'true' (boolean conversion to True)",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                translatable=False,
            ),
            item='translatable',
            new_value='true',
            exp_attrs=dict(
                translatable=True,
            ),
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMQUALIFIER_SETATTR)
@simplified_test_function
@log_entry_exit
def test_CIMQualifier_setattr(
        testcase, obj_kwargs, item, new_value, exp_attrs):
    """
    Test function for CIMQualifier set attribute
    """

    obj = CIMQualifier(**obj_kwargs)

    if isinstance(item, tuple):
        attr_name, attr_key = item
        attr_dict = getattr(obj, attr_name)

        # The code to be tested (for setting a single dict item)
        attr_dict[attr_key] = new_value

    else:
        attr_name = item

        # The code to be tested (for setting the entire attribute)
        setattr(obj, attr_name, new_value)

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

    for attr_name in exp_attrs:
        exp_value = exp_attrs[attr_name]
        value = getattr(obj, attr_name)
        assert value == exp_value


TESTCASES_CIMQUALIFIER_HASH_EQ = [

    # Testcases for CIMQualifier.__hash__() and __eq__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: CIMQualifier object #1 to be tested.
    #   * obj2: CIMQualifier object #2 to be tested.
    #   * exp_equal: Expected equality of the object hash values.
    # * 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

    # Name tests
    (
        "Name, equal with same lexical case",
        dict(
            obj1=CIMQualifier('Qual1', value=''),
            obj2=CIMQualifier('Qual1', value=''),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Name, equal with different lexical case",
        dict(
            obj1=CIMQualifier('Qual1', value=''),
            obj2=CIMQualifier('quAL1', value=''),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Name, different",
        dict(
            obj1=CIMQualifier('Qual1', value=''),
            obj2=CIMQualifier('Qual1_x', value=''),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Value tests
    (
        "Value, strings with different lexical case",
        dict(
            obj1=CIMQualifier('Qual1', value='abc'),
            obj2=CIMQualifier('Qual1', value='Abc'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Value, strings with None / string",
        dict(
            obj1=CIMQualifier('Qual1', value=None, type='string'),
            obj2=CIMQualifier('Qual1', value='abc', type='string'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Value, strings with string / None",
        dict(
            obj1=CIMQualifier('Qual1', value='abc', type='string'),
            obj2=CIMQualifier('Qual1', value=None, type='string'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Value, strings with None / None",
        dict(
            obj1=CIMQualifier('Qual1', value=None, type='string'),
            obj2=CIMQualifier('Qual1', value=None, type='string'),
            exp_equal=True,
        ),
        None, None, True
    ),

    # Type tests
    (
        "Type, different",
        dict(
            obj1=CIMQualifier('Qual1', value=7, type='uint8'),
            obj2=CIMQualifier('Qual1', value=7, type='sint8'),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Propagated tests
    (
        "Propagated, equal",
        dict(
            obj1=CIMQualifier('Qual1', value=None, type='string',
                              propagated=True),
            obj2=CIMQualifier('Qual1', value=None, type='string',
                              propagated=True),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Propagated, different",
        dict(
            obj1=CIMQualifier('Qual1', value=None, type='string',
                              propagated=True),
            obj2=CIMQualifier('Qual1', value=None, type='string',
                              propagated=False),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Overridable tests
    (
        "Overridable, equal",
        dict(
            obj1=CIMQualifier('Qual1', value=None, type='string',
                              overridable=True),
            obj2=CIMQualifier('Qual1', value=None, type='string',
                              overridable=True),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Overridable, different",
        dict(
            obj1=CIMQualifier('Qual1', value=None, type='string',
                              overridable=True),
            obj2=CIMQualifier('Qual1', value=None, type='string',
                              overridable=False),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Tosubclass tests
    (
        "Tosubclass, equal",
        dict(
            obj1=CIMQualifier('Qual1', value=None, type='string',
                              tosubclass=True),
            obj2=CIMQualifier('Qual1', value=None, type='string',
                              tosubclass=True),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Tosubclass, different",
        dict(
            obj1=CIMQualifier('Qual1', value=None, type='string',
                              tosubclass=True),
            obj2=CIMQualifier('Qual1', value=None, type='string',
                              tosubclass=False),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Toinstance tests
    (
        "Toinstance, equal",
        dict(
            obj1=CIMQualifier('Qual1', value=None, type='string',
                              toinstance=True),
            obj2=CIMQualifier('Qual1', value=None, type='string',
                              toinstance=True),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Toinstance, different",
        dict(
            obj1=CIMQualifier('Qual1', value=None, type='string',
                              toinstance=True),
            obj2=CIMQualifier('Qual1', value=None, type='string',
                              toinstance=False),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Translatable tests
    (
        "Translatable, equal",
        dict(
            obj1=CIMQualifier('Qual1', value=None, type='string',
                              translatable=True),
            obj2=CIMQualifier('Qual1', value=None, type='string',
                              translatable=True),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Translatable, different",
        dict(
            obj1=CIMQualifier('Qual1', value=None, type='string',
                              translatable=True),
            obj2=CIMQualifier('Qual1', value=None, type='string',
                              translatable=False),
            exp_equal=False,
        ),
        None, None, True
    ),
]

TESTCASES_CIMQUALIFIER_EQ = [

    # Additional testcases for CIMQualifier.__eq__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: CIMQualifier object #1 to be tested.
    #   * obj2: CIMQualifier object #2 to be tested.
    #   * exp_equal: Expected equality of the object hash values.
    # * 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

    # Exception testcases
    (
        "Invalid type of second object: string",
        dict(
            obj1=CIMQualifier('CIM_Foo', 'abc'),
            obj2='abc',
            exp_equal=None,
        ),
        TypeError, None, True
    ),
    (
        "Invalid type of second object: URI string",
        dict(
            obj1=CIMQualifier('CIM_Foo', 'abc'),
            obj2='http://abc:CIM_Foo',
            exp_equal=None,
        ),
        TypeError, None, True
    ),
    (
        "Invalid type of second object: CIMInstance",
        dict(
            obj1=CIMQualifier('CIM_Foo', Uint8(42)),
            obj2=42,
            exp_equal=None,
        ),
        TypeError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMQUALIFIER_HASH_EQ)
@simplified_test_function
@log_entry_exit
def test_CIMQualifier_hash(testcase, obj1, obj2, exp_equal):
    """
    Test function for CIMQualifier.__hash__().
    """

    # The code to be tested
    hash1 = hash(obj1)
    hash2 = hash(obj2)

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

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

    if exp_equal:
        assert hash1 == hash2
    else:
        assert hash1 != hash2


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMQUALIFIER_HASH_EQ + TESTCASES_CIMQUALIFIER_EQ)
@simplified_test_function
@log_entry_exit
def test_CIMQualifier_eq(testcase, obj1, obj2, exp_equal):
    """
    Test function for CIMQualifier.__eq__().
    """

    # The code to be tested
    equal = (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

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

    assert equal == exp_equal


TESTCASES_CIMQUALIFIER_STR_REPR = [

    # Testcases for CIMQualifier.__repr__(), __str__() / repr(), str()

    # Each list item is a testcase tuple with these items:
    # * obj: CIMQualifier object to be tested.
    (
        CIMQualifier('Spotty', 'Foot')
    ),
    (
        CIMQualifier('Revision', Real32(2.7))
    ),
    (
        CIMQualifier('RevisionList',
                     [Uint16(1), Uint16(2), Uint16(3)],
                     propagated=False)
    ),
]


@pytest.mark.parametrize(
    "obj",
    TESTCASES_CIMQUALIFIER_STR_REPR)
@log_entry_exit
def test_CIMQualifier_str(obj):
    """
    Test function for CIMQualifier.__str__() / str()
    """

    # The code to be tested
    s = str(obj)

    assert re.match(r'^CIMQualifier\(', s)

    exp_name = _format('name={0!A}', obj.name)
    assert exp_name in s

    exp_value = _format('value={0!A}', obj.value)
    assert exp_value in s

    exp_type = _format('type={0!A}', obj.type)
    assert exp_type in s


@pytest.mark.parametrize(
    "obj",
    TESTCASES_CIMQUALIFIER_STR_REPR)
@log_entry_exit
def test_CIMQualifier_repr(obj):
    """
    Test function for CIMQualifier.__repr__() / repr()
    """

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

    assert re.match(r'^CIMQualifier\(', r)

    exp_name = _format('name={0!A}', obj.name)
    assert exp_name in r

    exp_value = _format('value={0!A}', obj.value)
    assert exp_value in r

    exp_type = _format('type={0!A}', obj.type)
    assert exp_type in r

    exp_tosubclass = _format('tosubclass={0!A}', obj.tosubclass)
    assert exp_tosubclass in r

    exp_overridable = _format('overridable={0!A}', obj.overridable)
    assert exp_overridable in r

    exp_translatable = _format('translatable={0!A}', obj.translatable)
    assert exp_translatable in r

    exp_toinstance = _format('toinstance={0!A}', obj.toinstance)
    assert exp_toinstance in r

    exp_propagated = _format('propagated={0!A}', obj.propagated)
    assert exp_propagated in r


TESTCASES_CIMQUALIFIER_TOCIMXML = [

    # Testcases for CIMQualifier.tocimxml() and tocimxmlstr()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: CIMQualifier object to be tested.
    #   * kwargs: Dict of input args for tocimxml() (empty).
    #   * exp_xml_str: Expected CIM-XML string, as a tuple/list of parts.
    # * 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

    # Tests with name variations
    (
        "Name with ASCII characters, as byte string",
        dict(
            obj=CIMQualifier(b'Foo', value=None, type='string'),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Name with ASCII characters, as unicode string",
        dict(
            obj=CIMQualifier('Foo', value=None, type='string'),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Name with non-ASCII UCS-2 characters, as byte string",
        dict(
            obj=CIMQualifier(b'Foo\xC3\xA9', value=None, type='string'),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo\u00E9" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Name with non-ASCII UCS-2 characters, as unicode string",
        dict(
            obj=CIMQualifier('Foo\u00E9', value=None, type='string'),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo\u00E9" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Name with non-UCS-2 characters, as byte string",
        dict(
            obj=CIMQualifier(b'Foo\xF0\x90\x85\x82', value=None, type='string'),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo\U00010142" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Name with non-UCS-2 characters, as unicode string",
        dict(
            obj=CIMQualifier('Foo\U00010142', value=None, type='string'),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo\U00010142" TYPE="string"/>',
            )
        ),
        None, None, True
    ),

    # Variations of propagated argument
    (
        "Qualifier with propagated True",
        dict(
            obj=CIMQualifier(
                'Foo', type='boolean', value=None,
                propagated=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" PROPAGATED="true" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),
    (
        "Qualifier with propagated False",
        dict(
            obj=CIMQualifier(
                'Foo', type='boolean', value=None,
                propagated=False,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" PROPAGATED="false" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),

    # Variations of overridable argument
    (
        "Qualifier with overridable True",
        dict(
            obj=CIMQualifier(
                'Foo', type='boolean', value=None,
                overridable=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" OVERRIDABLE="true" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),
    (
        "Qualifier with overridable False",
        dict(
            obj=CIMQualifier(
                'Foo', type='boolean', value=None,
                overridable=False,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" OVERRIDABLE="false" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),

    # Variations of tosubclass argument
    (
        "Qualifier with tosubclass True",
        dict(
            obj=CIMQualifier(
                'Foo', type='boolean', value=None,
                tosubclass=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TOSUBCLASS="true" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),
    (
        "Qualifier with tosubclass False",
        dict(
            obj=CIMQualifier(
                'Foo', type='boolean', value=None,
                tosubclass=False,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TOSUBCLASS="false" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),

    # Variations of toinstance argument
    (
        "Qualifier with toinstance True",
        dict(
            obj=CIMQualifier(
                'Foo', type='boolean', value=None,
                toinstance=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TOINSTANCE="true" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),
    (
        "Qualifier with toinstance False",
        dict(
            obj=CIMQualifier(
                'Foo', type='boolean', value=None,
                toinstance=False,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TOINSTANCE="false" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),

    # Variations of translatable argument
    (
        "Qualifier with translatable True",
        dict(
            obj=CIMQualifier(
                'Foo', type='boolean', value=None,
                translatable=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TRANSLATABLE="true" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),
    (
        "Qualifier with translatable False",
        dict(
            obj=CIMQualifier(
                'Foo', type='boolean', value=None,
                translatable=False,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TRANSLATABLE="false" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifiers with boolean type
    (
        "Scalar qualifier with boolean type, value NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='boolean', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with boolean type, value True",
        dict(
            obj=CIMQualifier(
                'Foo', type='boolean', value=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="boolean">',
                '<VALUE>TRUE</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with boolean type, value False",
        dict(
            obj=CIMQualifier(
                'Foo', type='boolean', value=False,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="boolean">',
                '<VALUE>FALSE</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifiers with string type
    (
        "Scalar qualifier with string type, value NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='string', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with string type, value has one entry with ASCII "
        "characters",
        dict(
            obj=CIMQualifier(
                'Foo', type='string', value='foo',
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="string">',
                '<VALUE>foo</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with string type, value has one entry with "
        "non-ASCII UCS-2 characters",
        dict(
            obj=CIMQualifier(
                'Foo', type='string', value='foo\u00E9',
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="string">',
                '<VALUE>foo\u00E9</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with string type, value has one entry with "
        "non-UCS-2 characters",
        dict(
            obj=CIMQualifier(
                'Foo', type='string', value='foo\U00010142',
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="string">',
                '<VALUE>foo\U00010142</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifiers with char16 type
    (
        "Scalar qualifier with char16 type, value NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='char16', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="char16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with char16 type, value has one entry with an ASCII "
        "character",
        dict(
            obj=CIMQualifier(
                'Foo', type='char16', value='f',
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="char16">',
                '<VALUE>f</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with char16 type, value has one entry with a "
        "non-ASCII UCS-2 character",
        dict(
            obj=CIMQualifier(
                'Foo', type='char16', value='\u00E9',
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="char16">',
                '<VALUE>\u00E9</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with char16 type, value has one entry with "
        "non-UCS-2 character (invalid as per DSP0004, but tolerated by pywbem)",
        dict(
            obj=CIMQualifier(
                'Foo', type='char16', value='\U00010142',
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="char16">',
                '<VALUE>\U00010142</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifiers with uint8 type
    (
        "Scalar qualifier with uint8 type, value NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint8', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint8"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with uint8 type, value has one entry in range",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint8', value=42,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint8">',
                '<VALUE>42</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifiers with uint16 type
    (
        "Scalar qualifier with uint16 type, value NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint16', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with uint16 type, value has one entry in range",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint16', value=1234,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint16">',
                '<VALUE>1234</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifiers with uint32 type
    (
        "Scalar qualifier with uint32 type, value NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint32', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with uint32 type, value has one entry in range",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint32', value=12345678,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint32">',
                '<VALUE>12345678</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifiers with uint64 type
    (
        "Scalar qualifier with uint64 type, value NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint64', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with uint64 type, value has one entry in range",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint64', value=123456789012,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint64">',
                '<VALUE>123456789012</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifiers with sint8 type
    (
        "Scalar qualifier with sint8 type, value NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint8', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint8"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with sint8 type, value has one entry in range",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint8', value=-42,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint8">',
                '<VALUE>-42</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifiers with sint16 type
    (
        "Scalar qualifier with sint16 type, value NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint16', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with sint16 type, value has one entry in range",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint16', value=-1234,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint16">',
                '<VALUE>-1234</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifiers with sint32 type
    (
        "Scalar qualifier with sint32 type, value NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint32', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with sint32 type, value has one entry in range",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint32', value=-12345678,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint32">',
                '<VALUE>-12345678</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifiers with sint64 type
    (
        "Scalar qualifier with sint64 type, value NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint64', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with sint64 type, value has one entry in range",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint64', value=-123456789012,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint64">',
                '<VALUE>-123456789012</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifiers with real32 type
    (
        "Scalar qualifier with real32 type, value NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with real32 type, value between 0 and 1",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=0.42,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32">',
                '<VALUE>0.42</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with real32 type, value with max number of "
        "significant digits (11)",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=1.2345678901,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32">',
                '<VALUE>1.2345678901</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with real32 type, value larger 1 without exponent",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=42.0,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32">',
                '<VALUE>42.0</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with real32 type, value with small negative exponent",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=-42.0E-3,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32">',
                '<VALUE>-0.042</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with real32 type, value with small positive exponent",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=-42.0E+3,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32">',
                '<VALUE>-42000.0</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with real32 type, value with large negative exponent",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=-42.0E-30,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32">',
                '<VALUE>-4.2E-29</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with real32 type, value with large positive exponent",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=-42.0E+30,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32">',
                '<VALUE>-4.2E+31</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with real32 type, special value INF",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=float('inf'),
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32">',
                '<VALUE>INF</VALUE>',  # must be upper case
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with real32 type, special value -INF",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=float('-inf'),
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32">',
                '<VALUE>-INF</VALUE>',  # must be upper case
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with real32 type, special value NaN",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=float('nan'),
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32">',
                '<VALUE>NaN</VALUE>',  # must be upper case
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifiers with real64 type
    (
        "Scalar qualifier with real64 type, value NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with real64 type, value between 0 and 1",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=0.42,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64">',
                '<VALUE>0.42</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, False  # py27: 0.41999999999999998
    ),
    (
        "Scalar qualifier with real64 type, value with max number of "
        "significant digits (17)",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=1.2345678901234567,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64">',
                '<VALUE>1.2345678901234567</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with real64 type, value larger 1 without exponent",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=42.0,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64">',
                '<VALUE>42.0</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with real64 type, value with small negative exponent",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=-42.0E-3,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64">',
                '<VALUE>-0.042</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, False  # py27: -0.042000000000000003
    ),
    (
        "Scalar qualifier with real64 type, value with small positive exponent",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=-42.0E+3,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64">',
                '<VALUE>-42000.0</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with real64 type, value with large negative exponent",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=-42.0E-30,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64">',
                '<VALUE>-4.2E-29</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, False  # py27: -4.1999999999999998E-29
    ),
    (
        "Scalar qualifier with real64 type, value with large positive exponent",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=-42.0E+30,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64">',
                '<VALUE>-4.2E+31</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, False  # py27: -4.1999999999999996E+31
    ),
    (
        "Scalar qualifier with real64 type, special value INF",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=float('inf'),
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64">',
                '<VALUE>INF</VALUE>',  # must be upper case
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with real64 type, special value -INF",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=float('-inf'),
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64">',
                '<VALUE>-INF</VALUE>',  # must be upper case
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with real64 type, special value NaN",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=float('nan'),
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64">',
                '<VALUE>NaN</VALUE>',  # must be upper case
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifiers with datetime type
    (
        "Scalar qualifier with datetime type, value NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='datetime', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="datetime"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with datetime type, point in time value",
        dict(
            obj=CIMQualifier(
                'Foo', type='datetime',
                value=datetime(2014, 9, 22, 10, 49, 20, 524789),
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="datetime">',
                '<VALUE>20140922104920.524789+000</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier with datetime type, interval value",
        dict(
            obj=CIMQualifier(
                'Foo', type='datetime',
                value=timedelta(10, 49, 20),
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="datetime">',
                '<VALUE>00000010000049.000020:000</VALUE>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Qualifiers with reference type are not allowed as per DSP0004

    # Array qualifiers with boolean type
    (
        "Array qualifier with boolean type, value is NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='boolean', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with boolean type, value is empty array",
        dict(
            obj=CIMQualifier(
                'Foo', type='boolean', value=[],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="boolean">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with boolean type, value has one entry NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='boolean', value=[None],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="boolean">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with boolean type, value has one entry True",
        dict(
            obj=CIMQualifier(
                'Foo', type='boolean', value=[True],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="boolean">',
                '<VALUE.ARRAY>',
                '<VALUE>TRUE</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with boolean type, value has one entry False",
        dict(
            obj=CIMQualifier(
                'Foo', type='boolean', value=[False],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="boolean">',
                '<VALUE.ARRAY>',
                '<VALUE>FALSE</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Array qualifiers with string type
    (
        "Array qualifier with string type, value is NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='string', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with string type, value is empty array",
        dict(
            obj=CIMQualifier(
                'Foo', type='string', value=[],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="string">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with string type, value has one entry NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='string', value=[None],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="string">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with string type, value has one entry with ASCII "
        "characters",
        dict(
            obj=CIMQualifier(
                'Foo', type='string', value=['foo'],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="string">',
                '<VALUE.ARRAY>',
                '<VALUE>foo</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with string type, value has one entry with non-ASCII "
        "UCS-2 characters",
        dict(
            obj=CIMQualifier(
                'Foo', type='string', value=['foo\u00E9'],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="string">',
                '<VALUE.ARRAY>',
                '<VALUE>foo\u00E9</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with string type, value has one entry with non-UCS-2 "
        "characters",
        dict(
            obj=CIMQualifier(
                'Foo', type='string', value=['foo\U00010142'],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="string">',
                '<VALUE.ARRAY>',
                '<VALUE>foo\U00010142</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Array qualifiers with char16 type
    (
        "Array qualifier with char16 type, value is NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='char16', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="char16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with char16 type, value is empty array",
        dict(
            obj=CIMQualifier(
                'Foo', type='char16', value=[],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="char16">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with char16 type, value has one entry NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='char16', value=[None],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="char16">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with char16 type, value has one entry with an ASCII "
        "character",
        dict(
            obj=CIMQualifier(
                'Foo', type='char16', value=['f'],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="char16">',
                '<VALUE.ARRAY>',
                '<VALUE>f</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with char16 type, value has one entry with a "
        "non-ASCII UCS-2 character",
        dict(
            obj=CIMQualifier(
                'Foo', type='char16', value=['\u00E9'],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="char16">',
                '<VALUE.ARRAY>',
                '<VALUE>\u00E9</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with char16 type, value has one entry with a "
        "non-UCS-2 character (invalid as per DSP0004, but tolerated by pywbem)",
        dict(
            obj=CIMQualifier(
                'Foo', type='char16', value=['\U00010142'],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="char16">',
                '<VALUE.ARRAY>',
                '<VALUE>\U00010142</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Array qualifiers with uint8 type
    (
        "Array qualifier with uint8 type, value is NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint8', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint8"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with uint8 type, value is empty array",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint8', value=[],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint8">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with uint8 type, value has one entry NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint8', value=[None],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint8">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with uint8 type, value has one entry in range",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint8', value=[42],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint8">',
                '<VALUE.ARRAY>',
                '<VALUE>42</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Array qualifiers with uint16 type
    (
        "Array qualifier with uint16 type, value is NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint16', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with uint16 type, value is empty array",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint16', value=[],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint16">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with uint16 type, value has one entry NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint16', value=[None],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint16">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with uint16 type, value has one entry in range",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint16', value=[1234],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint16">',
                '<VALUE.ARRAY>',
                '<VALUE>1234</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Array qualifiers with uint32 type
    (
        "Array qualifier with uint32 type, value is NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint32', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with uint32 type, value is empty array",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint32', value=[],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint32">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with uint32 type, value has one entry NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint32', value=[None],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint32">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with uint32 type, value has one entry in range",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint32', value=[12345678],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint32">',
                '<VALUE.ARRAY>',
                '<VALUE>12345678</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Array qualifiers with uint64 type
    (
        "Array qualifier with uint64 type, value is NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint64', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with uint64 type, value is empty array",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint64', value=[],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint64">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with uint64 type, value has one entry NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint64', value=[None],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint64">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with uint64 type, value has one entry in range",
        dict(
            obj=CIMQualifier(
                'Foo', type='uint64', value=[123456789012],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="uint64">',
                '<VALUE.ARRAY>',
                '<VALUE>123456789012</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Array qualifiers with sint8 type
    (
        "Array qualifier with sint8 type, value is NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint8', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint8"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with sint8 type, value is empty array",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint8', value=[],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint8">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with sint8 type, value has one entry NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint8', value=[None],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint8">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with sint8 type, value has one entry in range",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint8', value=[-42],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint8">',
                '<VALUE.ARRAY>',
                '<VALUE>-42</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Array qualifiers with sint16 type
    (
        "Array qualifier with sint16 type, value is NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint16', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with sint16 type, value is empty array",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint16', value=[],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint16">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with sint16 type, value has one entry NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint16', value=[None],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint16">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with sint16 type, value has one entry in range",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint16', value=[-1234],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint16">',
                '<VALUE.ARRAY>',
                '<VALUE>-1234</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Array qualifiers with sint32 type
    (
        "Array qualifier with sint32 type, value is NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint32', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with sint32 type, value is empty array",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint32', value=[],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint32">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with sint32 type, value has one entry NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint32', value=[None],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint32">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with sint32 type, value has one entry in range",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint32', value=[-12345678],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint32">',
                '<VALUE.ARRAY>',
                '<VALUE>-12345678</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Array qualifiers with sint64 type
    (
        "Array qualifier with sint64 type, value is NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint64', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with sint64 type, value is empty array",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint64', value=[],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint64">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with sint64 type, value has one entry NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint64', value=[None],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint64">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with sint64 type, value has one entry in range",
        dict(
            obj=CIMQualifier(
                'Foo', type='sint64', value=[-123456789012],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="sint64">',
                '<VALUE.ARRAY>',
                '<VALUE>-123456789012</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Array qualifiers with real32 type
    (
        "Array qualifier with real32 type, value is NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with real32 type, value is empty array",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=[],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with real32 type, value has one entry NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=[None],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with real32 type, value has one entry between 0 and 1",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=[0.42],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>0.42</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with real32 type, value has one entry with max "
        "number of significant digits (11)",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=[1.2345678901],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>1.2345678901</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with real32 type, value has one entry larger 1 "
        "without exponent",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=[42.0],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>42.0</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with real32 type, value has one entry with small "
        "negative exponent",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=[-42.0E-3],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>-0.042</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with real32 type, value has one entry with small "
        "positive exponent",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=[-42.0E+3],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>-42000.0</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with real32 type, value has one entry with large "
        "negative exponent",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=[-42.0E-30],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>-4.2E-29</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with real32 type, value has one entry with large "
        "positive exponent",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=[-42.0E+30],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>-4.2E+31</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with real32 type, value has one entry that is "
        "special value INF",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=[float('inf')],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>INF</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with real32 type, value has one entry that is "
        "special value -INF",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=[float('-inf')],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>-INF</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with real32 type, value has one entry that is "
        "special value NaN",
        dict(
            obj=CIMQualifier(
                'Foo', type='real32', value=[float('nan')],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>NaN</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Array qualifiers with real64 type
    (
        "Array qualifier with real64 type, value is NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with real64 type, value is empty array",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=[],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with real64 type, value has one entry NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=[None],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with real64 type, value has one entry between 0 and 1",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=[0.42],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>0.42</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, False  # py27: 0.41999999999999998
    ),
    (
        "Array qualifier with real64 type, value has one entry with max "
        "number of significant digits (17)",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=[1.2345678901234567],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>1.2345678901234567</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with real64 type, value has one entry larger 1 "
        "without exponent",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=[42.0],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>42.0</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with real64 type, value has one entry with small "
        "negative exponent",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=[-42.0E-3],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>-0.042</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, False  # py27: -0.042000000000000003
    ),
    (
        "Array qualifier with real64 type, value has one entry with small "
        "positive exponent",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=[-42.0E+3],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>-42000.0</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with real64 type, value has one entry with large "
        "negative exponent",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=[-42.0E-30],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>-4.2E-29</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, False  # py27: -4.1999999999999998E-29
    ),
    (
        "Array qualifier with real64 type, value has one entry with large "
        "positive exponent",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=[-42.0E+30],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>-4.2E+31</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, False  # py27: -4.1999999999999996E+31
    ),
    (
        "Array qualifier with real64 type, value has one entry that is "
        "special value INF",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=[float('inf')],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>INF</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with real64 type, value has one entry that is "
        "special value -INF",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=[float('-inf')],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>-INF</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with real64 type, value has one entry that is "
        "special value NaN",
        dict(
            obj=CIMQualifier(
                'Foo', type='real64', value=[float('nan')],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>NaN</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),

    # Array qualifiers with datetime type
    (
        "Array qualifier with datetime type, value is NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='datetime', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="datetime"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with datetime type, value is empty array",
        dict(
            obj=CIMQualifier(
                'Foo', type='datetime', value=[],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="datetime">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with datetime type, value has one entry NULL",
        dict(
            obj=CIMQualifier(
                'Foo', type='datetime', value=[None],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="datetime">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with datetime type, value has one entry point in time",
        dict(
            obj=CIMQualifier(
                'Foo', type='datetime',
                value=[datetime(2014, 9, 22, 10, 49, 20, 524789)],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="datetime">',
                '<VALUE.ARRAY>',
                '<VALUE>20140922104920.524789+000</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier with datetime type, value has one entry interval",
        dict(
            obj=CIMQualifier(
                'Foo', type='datetime',
                value=[timedelta(10, 49, 20)],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER NAME="Foo" TYPE="datetime">',
                '<VALUE.ARRAY>',
                '<VALUE>00000010000049.000020:000</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER>',
            )
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMQUALIFIER_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMQualifier_tocimxml(testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMQualifier.tocimxml().
    """

    # The code to be tested
    obj_xml = obj.tocimxml(**kwargs)

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

    obj_xml_str = obj_xml.toxml()
    exp_xml_str = ''.join(exp_xml_str)
    validate_cim_xml_obj(obj, obj_xml_str, exp_xml_str)


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMQUALIFIER_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMQualifier_tocimxmlstr(testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMQualifier.tocimxmlstr().
    """

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(**kwargs)

    # 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(obj_xml_str, str)

    exp_xml_str = ''.join(exp_xml_str)
    validate_cim_xml_obj(obj, obj_xml_str, exp_xml_str)


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMQUALIFIER_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMQualifier_tocimxmlstr_indent_int(
        testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMQualifier.tocimxmlstr() with indent as integer.
    """

    indent = 4
    indent_str = ' ' * indent

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(indent=indent, **kwargs)

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

    obj_xml = obj.tocimxml(**kwargs)  # This is tested elsewhere
    exp_xml_str = obj_xml.toprettyxml(indent=indent_str)

    assert obj_xml_str == exp_xml_str, (
        f"{obj.__class__.__name__}.tocimxmlstr(indent={indent!r}) "
        "returns unexpected CIM-XML string")


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMQUALIFIER_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMQualifier_tocimxmlstr_indent_str(
        testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMQualifier.tocimxmlstr() with indent as string.
    """

    indent = 4
    indent_str = ' ' * indent

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(indent=indent_str, **kwargs)

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

    obj_xml = obj.tocimxml(**kwargs)  # This is tested elsewhere
    exp_xml_str = obj_xml.toprettyxml(indent=indent_str)

    assert obj_xml_str == exp_xml_str, (
        f"{obj.__class__.__name__}.tocimxmlstr(indent={indent_str!r}) "
        "returns unexpected CIM-XML string")


TESTCASES_CIMQUALIFIER_TOMOF = [

    # Testcases for CIMQualifier.tomof()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: CIMQualifier object to be tested.
    #   * kwargs: Dict of input args to tomof() method
    #   * exp_mof: Expected MOF result string, or None if failed.
    # * 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

    (
        "all components",
        dict(
            obj=CIMQualifier(
                name='Q1',
                value=["abc"],
                type='string',
                propagated=True,
                overridable=True,
                tosubclass=True,
                toinstance=True,
                translatable=True,
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""Q1 { "abc" }""",
        ),
        None, None, True
    ),
    (
        "string type, NULL value",
        dict(
            obj=CIMQualifier(
                name='Q1',
                value=None,
                type='string',
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""Q1 ( NULL )""",
        ),
        None, None, True
    ),
    (
        "string type, value with escape sequences dq,sq,bs",
        dict(
            obj=CIMQualifier(
                name='Q1',
                value="dq=\",sq=\',bs=\\",
                type='string',
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""Q1 ( "dq=\\",sq=\\',bs=\\\\" )""",
        ),
        None, None, True
    ),
    (
        "string type, value with escape sequences bt,tb,nl",
        dict(
            obj=CIMQualifier(
                name='Q1',
                value="bt=\b,tb=\t,nl=\n",
                type='string',
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""Q1 ( "bt=\\b,tb=\\t,nl=\\n" )""",
        ),
        None, None, True
    ),
    (
        "string type, value with escape sequences vt,cr",
        dict(
            obj=CIMQualifier(
                name='Q1',
                value="vt=\f,cr=\r",
                type='string',
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""Q1 ( "vt=\\f,cr=\\r" )""",
        ),
        None, None, True
    ),
    (
        "string array that is empty",
        dict(
            obj=CIMQualifier(
                name='Q1',
                value=[],
                type='string',
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""Q1 { }""",
        ),
        None, None, True
    ),
    (
        "string array with a value of two items",
        dict(
            obj=CIMQualifier(
                name='Q1',
                value=["abc", "def"],
                type='string',
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""Q1 { "abc", "def" }""",
        ),
        None, None, True
    ),
    (
        "string array with a value of two items with one being None",
        dict(
            obj=CIMQualifier(
                name='Q1',
                value=["abc", None],
                type='string',
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""Q1 { "abc", NULL }""",
        ),
        None, None, True
    ),
    (
        "string type with multi line value",
        dict(
            obj=CIMQualifier(
                name='Q1',
                value=('abc def ' * 10 + 'z'),
                type='string',
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""Q1 (
            "abc def abc def abc def abc def abc def abc def abc def abc def "
            "abc def abc def z" )""",
        ),
        None, None, True
    ),

    # pylint: disable=line-too-long

    (
        "string array type with multi line value with short items",
        dict(
            obj=CIMQualifier(
                name='Q1',
                value=[f'abcdef{_i:02d}' for _i in range(0, 10)],
                type='string',
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""Q1 { "abcdef00", "abcdef01", "abcdef02", "abcdef03", "abcdef04", "abcdef05",
            "abcdef06", "abcdef07", "abcdef08", "abcdef09" }""",  # noqa: E501
        ),
        None, None, True
    ),

    # pylint: enable=line-too-long

    (
        "string array type with with long items",
        dict(
            obj=CIMQualifier(
                name='Q1',
                value=['abc def ' * 10 + f'z{_i:02d}' for _i in range(0, 2)],
                type='string',
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""Q1 {
            "abc def abc def abc def abc def abc def abc def abc def abc def "
            "abc def abc def z00",
            "abc def abc def abc def abc def abc def abc def abc def abc def "
            "abc def abc def z01" }""",
        ),
        None, None, True
    ),
    (
        "char16 type, value nl",
        dict(
            obj=CIMQualifier(
                name='Q1',
                value="\n",
                type='char16',
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""Q1 ( '\\n' ) """,
        ),
        None, None, False
        # TODO 01/18 AM Enable test case once char16 produces single quotes
    ),
    (
        "boolean type, value False",
        dict(
            obj=CIMQualifier(
                name='Q1',
                value=False,
                type='boolean',
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""Q1 ( false )""",
        ),
        None, None, True
    ),
    (
        "uint32 type, value 42",
        dict(
            obj=CIMQualifier(
                name='Q1',
                value=Uint32(42),
                type='uint32',
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""Q1 ( 42 )""",
        ),
        None, None, True
    ),
    (
        "real32 type, value 42.1",
        dict(
            obj=CIMQualifier(
                name='Q1',
                value=Real32(42.1),
                type='real32',
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""Q1 ( 42.1 )""",
        ),
        # Unpredictable string before 0.12
        None, None, True
    ),
    (
        "datetime type, with a value",
        dict(
            obj=CIMQualifier(
                name='Q1',
                value=CIMDateTime('20140924193040.654321+120'),
                type='datetime',
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""Q1 ( "20140924193040.654321+120" )""",
        ),
        None, None, True
    ),
    (
        "flavors ToSubclass EnableOverride",
        dict(
            obj=CIMQualifier(
                name='Q1',
                value='',
                type='string',
                overridable=True,
                tosubclass=True,
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""Q1 ( "" )""",
        ),
        None, None, True
    ),
    (
        "flavor ToSubclass DisableOverride",
        dict(
            obj=CIMQualifier(
                name='Q1',
                value='',
                type='string',
                overridable=False,
                tosubclass=True,
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""Q1 ( "" )""",
        ),
        None, None, True
    ),
    (
        "flavor Restricted",
        dict(
            obj=CIMQualifier(
                name='Q1',
                value='',
                type='string',
                tosubclass=False,
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""Q1 ( "" )""",
        ),
        None, None, True
    ),
    (
        "flavor Translatable",
        dict(
            obj=CIMQualifier(
                name='Q1',
                value='',
                type='string',
                translatable=True,
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""Q1 ( "" )""",
        ),
        None, None, True
    ),
    (
        "flavor ToInstance (not in DSP0004)",
        dict(
            obj=CIMQualifier(
                name='Q1',
                value='',
                type='string',
                toinstance=True,
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""Q1 ( "" )""",
        ),
        None, None, True
    ),
    (
        "Qualifier with name that does not fit onto line by 10",
        dict(
            obj=CIMQualifier('Very_long_qualifier_name', value='abc'),
            kwargs=dict(
                indent=3,
                maxline=16,
            ),
            exp_mof='Very_long_qualifier_name (\n   "abc" )',
        ),
        None, None, True
    ),
    (
        "uint64 qualifier value that does not fit onto line by 1",
        dict(
            obj=CIMQualifier('Q1', value=Uint64(1998012513304050)),
            kwargs=dict(
                indent=3,
                maxline=21,
            ),
            exp_mof=None,
        ),
        ValueError, None, True
    ),
    (
        "uint64 qualifier value that exactly fits onto line",
        dict(
            obj=CIMQualifier('Q1', value=Uint64(1998012513304050)),
            kwargs=dict(
                indent=3,
                maxline=22,
            ),
            exp_mof=''
            'Q1 (\n'
            '   1998012513304050 )',
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMQUALIFIER_TOMOF)
@simplified_test_function
@log_entry_exit
def test_CIMQualifier_tomof(testcase, obj, kwargs, exp_mof):
    """
    Test function for CIMQualifier.tomof().
    """

    # The code to be tested
    mof = obj.tomof(**kwargs)

    # 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(mof, str)
    assert mof == exp_mof


TESTCASES_CIMCLASSNAME_INIT = [

    # Testcases for CIMClassName.__init__()

    # 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 CIMClassName().
    #   * init_kwargs: Dict of keyword arguments to CIMClassName().
    #   * exp_attrs: Dict of expected attributes of resulting 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

    # Order of positional arguments
    (
        "Verify order of positional arguments",
        dict(
            init_args=[
                'CIM_Foo',
                'woot.com',
                'cimv2',
            ],
            init_kwargs={},
            exp_attrs=dict(
                classname='CIM_Foo',
                host='woot.com',
                namespace='cimv2',
            ),
        ),
        None, None, True
    ),

    # Classname tests
    (
        "Verify that bytes classname is converted to unicode",
        dict(
            init_args=[],
            init_kwargs=dict(classname=b'CIM_Foo'),
            exp_attrs=dict(classname='CIM_Foo')
        ),
        None, None, True
    ),
    (
        "Verify that unicode classname remains unicode",
        dict(
            init_args=[],
            init_kwargs=dict(classname='CIM_Foo'),
            exp_attrs=dict(classname='CIM_Foo')
        ),
        None, None, True
    ),

    # Namespace tests
    (
        "Verify that bytes namespace is converted to unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                namespace=b'root/cimv2'
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2'
            )
        ),
        None, None, True
    ),
    (
        "Verify that unicode namespace remains unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2'
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2'
            )
        ),
        None, None, True
    ),
    (
        "Verify that one leading and trailing slash in namespace get "
        "stripped",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                namespace='/root/cimv2/'
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2'
            )
        ),
        None, None, True
    ),
    (
        "Verify that two leading and trailing slashes in namespace get "
        "stripped",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                namespace='//root/cimv2//'
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2'
            )
        ),
        None, None, True
    ),

    # Host tests
    (
        "Verify that bytes host is converted to unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host=b'woot.com'
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='woot.com'
            )
        ),
        None, None, True
    ),
    (
        "Verify that unicode host remains unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='woot.com'
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='woot.com'
            )
        ),
        None, None, True
    ),

    # Exception testcases
    (
        "Verify that classname None fails",
        dict(
            init_args=[],
            init_kwargs=dict(classname=None),
            exp_attrs=None
        ),
        # raises ValueError instead of TypeError since 0.12
        ValueError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMCLASSNAME_INIT)
@simplified_test_function
@log_entry_exit
def test_CIMClassName_init(testcase, init_args, init_kwargs, exp_attrs):
    """
    Test function for CIMClassName.__init__()
    """

    # The code to be tested
    obj = CIMClassName(*init_args, **init_kwargs)

    assert not hasattr(obj, '__dict__')

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

    exp_classname = exp_attrs['classname']
    assert obj.classname == exp_classname
    assert isinstance(obj.classname, type(exp_classname))

    exp_host = exp_attrs.get('host', None)
    assert obj.host == exp_host
    assert isinstance(obj.host, type(exp_host))

    exp_namespace = exp_attrs.get('namespace', None)
    assert obj.namespace == exp_namespace
    assert isinstance(obj.namespace, type(exp_namespace))


TESTCASES_CIMCLASSNAME_COPY = [

    # Testcases for CIMClassName.copy()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj_kwargs: Dict of init kwargs for original CIMClassName.
    # * 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

    (
        "All attributes set",
        dict(
            obj_kwargs=dict(
                classname='CIM_Foo',
                host='woot.com',
                namespace='cimv2',
            )
        ),
        None, None, True
    ),
    (
        "No Host",
        dict(
            obj_kwargs=dict(
                classname='CIM_Foo',
                host=None,
                namespace='cimv2',
            )
        ),
        None, None, True
    ),
    (
        "No Namespace",
        dict(
            obj_kwargs=dict(
                classname='CIM_Foo',
                host='woot.com',
                namespace=None,
            )
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMCLASSNAME_COPY)
@simplified_test_function
@log_entry_exit
def test_CIMClassName_copy(testcase, obj_kwargs):
    """
    Test function for CIMClassName.copy()
    """

    obj1 = CIMClassName(**obj_kwargs)

    # The code to be tested
    obj2 = obj1.copy()

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

    # Verify that the copy is equal to the original (performs deep equality)
    assert obj2 == obj1

    # Verify that the copy is a different object
    assert id(obj2) != id(obj1)

    # No mutable child objects to verify for being different objects

    # Verify that the copy can be modified and the original remains unchanged

    obj1_classname = obj1.classname
    obj2.classname = 'SomeNewClassname'
    assert obj1.classname == obj1_classname

    obj1_host = obj1.host
    obj2.host = 'SomeNewHost'
    assert obj1.host == obj1_host

    obj1_namespace = obj1.namespace
    obj2.namespace = 'SomeNewNamespace'
    assert obj1.namespace == obj1_namespace


CIMCLASSNAME_SETATTR_C1_KWARGS = dict(
    classname='C1',
    host='woot.com',
    namespace='cimv2',
)

TESTCASES_CIMCLASSNAME_SETATTR = [

    # Testcases for CIMClassName set attribute

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj_kwargs: Dict of init kwargs for CIMClassName.
    #   * item: Name of CIMClassName attr to set, or tuple
    #     (item, key) to set a single item in an attr that is a dict.
    #   * new_value: New value to set.
    #   * exp_attrs: Dict of expected attributes, for validation.
    # * 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

    # Tests that set the classname attribute
    (
        "Set classname to different string",
        dict(
            obj_kwargs=CIMCLASSNAME_SETATTR_C1_KWARGS,
            item='classname',
            new_value='CIM_Bar',
            exp_attrs=dict(
                classname='CIM_Bar',
            ),
        ),
        None, None, True
    ),
    (
        "Set classname to None",
        dict(
            obj_kwargs=CIMCLASSNAME_SETATTR_C1_KWARGS,
            item='classname',
            new_value=None,
            exp_attrs=dict(
                classname=None,
            ),
        ),
        # raises ValueError since 0.12
        ValueError, None, True
    ),

    # Tests that set the host attribute
    (
        "Set host to different string",
        dict(
            obj_kwargs=CIMCLASSNAME_SETATTR_C1_KWARGS,
            item='host',
            new_value='foo',
            exp_attrs=dict(
                host='foo',
            ),
        ),
        None, None, True
    ),
    (
        "Set host to None",
        dict(
            obj_kwargs=CIMCLASSNAME_SETATTR_C1_KWARGS,
            item='host',
            new_value=None,
            exp_attrs=dict(
                host=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the namespace attribute
    (
        "Set namespace to different string",
        dict(
            obj_kwargs=CIMCLASSNAME_SETATTR_C1_KWARGS,
            item='namespace',
            new_value='foo',
            exp_attrs=dict(
                namespace='foo',
            ),
        ),
        None, None, True
    ),
    (
        "Set namespace to None",
        dict(
            obj_kwargs=CIMCLASSNAME_SETATTR_C1_KWARGS,
            item='namespace',
            new_value=None,
            exp_attrs=dict(
                namespace=None,
            ),
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMCLASSNAME_SETATTR)
@simplified_test_function
@log_entry_exit
def test_CIMClassName_setattr(
        testcase, obj_kwargs, item, new_value, exp_attrs):
    """
    Test function for CIMClassName set attribute
    """

    obj = CIMClassName(**obj_kwargs)

    if isinstance(item, tuple):
        attr_name, attr_key = item
        attr_dict = getattr(obj, attr_name)

        # The code to be tested (for setting a single dict item)
        attr_dict[attr_key] = new_value

    else:
        attr_name = item

        # The code to be tested (for setting the entire attribute)
        setattr(obj, attr_name, new_value)

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

    for attr_name in exp_attrs:
        exp_value = exp_attrs[attr_name]
        value = getattr(obj, attr_name)
        assert value == exp_value


TESTCASES_CIMCLASSNAME_HASH_EQ = [

    # Testcases for CIMClassName.__hash__() and __eq__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: CIMClassName object #1 to be tested.
    #   * obj2: CIMClassName object #2 to be tested.
    #   * exp_equal: Expected equality of the object hash values.
    # * 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

    # Classname tests
    (
        "Classname, equal with same lexical case",
        dict(
            obj1=CIMClassName('CIM_Foo'),
            obj2=CIMClassName('CIM_Foo'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Classname, equal with different lexical case",
        dict(
            obj1=CIMClassName('CIM_Foo'),
            obj2=CIMClassName('ciM_foO'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Classname, different",
        dict(
            obj1=CIMClassName('CIM_Foo'),
            obj2=CIMClassName('CIM_Foo_x'),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Host tests
    (
        "Host name, equal with same lexical case",
        dict(
            obj1=CIMClassName('CIM_Foo', host='woot.com'),
            obj2=CIMClassName('CIM_Foo', host='woot.Com'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Host name, equal with different lexical case",
        dict(
            obj1=CIMClassName('CIM_Foo', host='woot.com'),
            obj2=CIMClassName('CIM_Foo', host='Woot.Com'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Host name, different with None / string",
        dict(
            obj1=CIMClassName('CIM_Foo', host=None),
            obj2=CIMClassName('CIM_Foo', host='woot.com'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Host name, different with string / None",
        dict(
            obj1=CIMClassName('CIM_Foo', host='woot.com'),
            obj2=CIMClassName('CIM_Foo', host=None),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Host name, equal with None / None",
        dict(
            obj1=CIMClassName('CIM_Foo', host=None),
            obj2=CIMClassName('CIM_Foo', host=None),
            exp_equal=True,
        ),
        None, None, True
    ),

    # Namespace tests
    (
        "Namespace, equal with same lexical case",
        dict(
            obj1=CIMClassName('CIM_Foo', namespace='root/cimv2'),
            obj2=CIMClassName('CIM_Foo', namespace='root/cimv2'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Namespace, equal with different lexical case",
        dict(
            obj1=CIMClassName('CIM_Foo', namespace='root/cimv2'),
            obj2=CIMClassName('CIM_Foo', namespace='Root/CIMv2'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Namespace, different",
        dict(
            obj1=CIMClassName('CIM_Foo', namespace='root/cimv2'),
            obj2=CIMClassName('CIM_Foo', namespace='abc'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Namespace, different with None / string",
        dict(
            obj1=CIMClassName('CIM_Foo', namespace=None),
            obj2=CIMClassName('CIM_Foo', namespace='root/cimv2'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Namespace, different with string / None",
        dict(
            obj1=CIMClassName('CIM_Foo', namespace='root/cimv2'),
            obj2=CIMClassName('CIM_Foo', namespace=None),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Namespace, equal with None / None",
        dict(
            obj1=CIMClassName('CIM_Foo', namespace=None),
            obj2=CIMClassName('CIM_Foo', namespace=None),
            exp_equal=True,
        ),
        None, None, True
    ),
]

TESTCASES_CIMCLASSNAME_EQ = [

    # Additional testcases for CIMClassName.__eq__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: CIMClassName object #1 to be tested.
    #   * obj2: CIMClassName object #2 to be tested.
    #   * exp_equal: Expected equality of the object hash values.
    # * 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

    # Exception testcases
    (
        "Invalid type of second object: string",
        dict(
            obj1=CIMClassName('CIM_Foo'),
            obj2='abc',
            exp_equal=None,
        ),
        TypeError, None, True
    ),
    (
        "Invalid type of second object: URI string",
        dict(
            obj1=CIMClassName('CIM_Foo'),
            obj2='http://abc:CIM_Foo',
            exp_equal=None,
        ),
        TypeError, None, True
    ),
    (
        "Invalid type of second object: CIMInstance",
        dict(
            obj1=CIMClassName('CIM_Foo', Uint8(42)),
            obj2=42,
            exp_equal=None,
        ),
        TypeError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMCLASSNAME_HASH_EQ)
@simplified_test_function
@log_entry_exit
def test_CIMClassName_hash(testcase, obj1, obj2, exp_equal):
    """
    Test function for CIMClassName.__hash__().
    """

    # The code to be tested
    hash1 = hash(obj1)
    hash2 = hash(obj2)

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

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

    if exp_equal:
        assert hash1 == hash2
    else:
        assert hash1 != hash2


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMCLASSNAME_HASH_EQ + TESTCASES_CIMCLASSNAME_EQ)
@simplified_test_function
@log_entry_exit
def test_CIMClassName_eq(testcase, obj1, obj2, exp_equal):
    """
    Test function for CIMClassName.__eq__().
    """

    # The code to be tested
    equal = (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

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

    assert equal == exp_equal


TESTCASES_CIMCLASSNAME_REPR = [

    # Testcases for CIMClassName.__repr__() / repr()
    # Note: CIMClassName.__str__() is tested along with to_wbem_uri()

    # Each list item is a testcase tuple with these items:
    # * obj: CIMClassName object to be tested.

    (
        CIMClassName(
            classname='CIM_Foo')
    ),
    (
        CIMClassName(
            classname='CIM_Foo',
            namespace='cimv2')
    ),
    (
        CIMClassName(
            classname='CIM_Foo',
            namespace='cimv2',
            host='10.11.12.13')
    ),
    (
        CIMClassName(
            classname='CIM_Foo',
            namespace='root/cimv2',
            host='10.11.12.13')
    ),
    (
        CIMClassName(
            classname='CIM_Foo',
            namespace='root/cimv2',
            host='10.11.12.13:5989')
    ),
    (
        CIMClassName(
            classname='CIM_Foo',
            namespace='root/cimv2',
            host='jdd:test@10.11.12.13:5989')
    ),
]


@pytest.mark.parametrize(
    "obj",
    TESTCASES_CIMCLASSNAME_REPR)
@log_entry_exit
def test_CIMClassName_repr(obj):
    """
    Test function for CIMClassName.__repr__() / repr()

    Note: CIMClassName.__str__() is tested along with to_wbem_uri()
    """

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

    assert re.match(r'^CIMClassName\(', r)

    exp_classname = _format('classname={0!A}', obj.classname)
    assert exp_classname in r

    exp_namespace = _format('namespace={0!A}', obj.namespace)
    assert exp_namespace in r

    exp_host = _format('host={0!A}', obj.host)
    assert exp_host in r


TESTCASES_CIMCLASSNAME_TOCIMXML = [

    # Testcases for CIMClassName.tocimxml() and tocimxmlstr()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: CIMClassName object to be tested.
    #   * kwargs: Dict of input args for tocimxml().
    #   * exp_xml_str: Expected CIM-XML string, as a tuple/list of parts.
    # * 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

    # Classname-only tests
    (
        "Classname only, with implied default args",
        dict(
            obj=CIMClassName('CIM_Foo'),
            kwargs={},
            exp_xml_str=(
                '<CLASSNAME NAME="CIM_Foo"/>',
            )
        ),
        None, None, True
    ),
    (
        "Classname only, with specified default args",
        dict(
            obj=CIMClassName('CIM_Foo'),
            kwargs=dict(
                ignore_host=False,
                ignore_namespace=False,
            ),
            exp_xml_str=(
                '<CLASSNAME NAME="CIM_Foo"/>',
            )
        ),
        None, None, True
    ),
    (
        "Classname only, with non-default args: ignore_host=True",
        dict(
            obj=CIMClassName('CIM_Foo'),
            kwargs=dict(
                ignore_host=True,
            ),
            exp_xml_str=(
                '<CLASSNAME NAME="CIM_Foo"/>',
            )
        ),
        None, None, True
    ),
    (
        "Classname only, with non-default args: ignore_namespace=True",
        dict(
            obj=CIMClassName('CIM_Foo'),
            kwargs=dict(
                ignore_namespace=True,
            ),
            exp_xml_str=(
                '<CLASSNAME NAME="CIM_Foo"/>',
            )
        ),
        None, None, True
    ),

    # Classname with namespace tests
    (
        "Classname with namespace, with implied default args",
        dict(
            obj=CIMClassName(
                'CIM_Foo',
                namespace='root/cimv2',
            ),
            kwargs={},
            exp_xml_str=(
                '<LOCALCLASSPATH>',
                '<LOCALNAMESPACEPATH>',
                '<NAMESPACE NAME="root"/>',
                '<NAMESPACE NAME="cimv2"/>',
                '</LOCALNAMESPACEPATH>',
                '<CLASSNAME NAME="CIM_Foo"/>',
                '</LOCALCLASSPATH>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with namespace, with specified default args",
        dict(
            obj=CIMClassName(
                'CIM_Foo',
                namespace='root/cimv2',
            ),
            kwargs=dict(
                ignore_host=False,
                ignore_namespace=False,
            ),
            exp_xml_str=(
                '<LOCALCLASSPATH>',
                '<LOCALNAMESPACEPATH>',
                '<NAMESPACE NAME="root"/>',
                '<NAMESPACE NAME="cimv2"/>',
                '</LOCALNAMESPACEPATH>',
                '<CLASSNAME NAME="CIM_Foo"/>',
                '</LOCALCLASSPATH>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with namespace, with non-default: ignore_host=True",
        dict(
            obj=CIMClassName(
                'CIM_Foo',
                namespace='root/cimv2',
            ),
            kwargs=dict(
                ignore_host=True,
            ),
            exp_xml_str=(
                '<LOCALCLASSPATH>',
                '<LOCALNAMESPACEPATH>',
                '<NAMESPACE NAME="root"/>',
                '<NAMESPACE NAME="cimv2"/>',
                '</LOCALNAMESPACEPATH>',
                '<CLASSNAME NAME="CIM_Foo"/>',
                '</LOCALCLASSPATH>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with namespace, with non-def: ignore_namespace=True",
        dict(
            obj=CIMClassName(
                'CIM_Foo',
                namespace='root/cimv2',
            ),
            kwargs=dict(
                ignore_namespace=True,
            ),
            exp_xml_str=(
                '<CLASSNAME NAME="CIM_Foo"/>',
            )
        ),
        None, None, True
    ),

    # Classname with namespace+host tests
    (
        "Classname with namespace+host, with implied default args",
        dict(
            obj=CIMClassName(
                'CIM_Foo',
                namespace='root/cimv2',
                host='woot.com',
            ),
            kwargs={},
            exp_xml_str=(
                '<CLASSPATH>',
                '<NAMESPACEPATH>',
                '<HOST>woot.com</HOST>',
                '<LOCALNAMESPACEPATH>',
                '<NAMESPACE NAME="root"/>',
                '<NAMESPACE NAME="cimv2"/>',
                '</LOCALNAMESPACEPATH>',
                '</NAMESPACEPATH>',
                '<CLASSNAME NAME="CIM_Foo"/>',
                '</CLASSPATH>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with namespace+host, with specified default args",
        dict(
            obj=CIMClassName(
                'CIM_Foo',
                namespace='root/cimv2',
                host='woot.com',
            ),
            kwargs=dict(
                ignore_host=False,
                ignore_namespace=False,
            ),
            exp_xml_str=(
                '<CLASSPATH>',
                '<NAMESPACEPATH>',
                '<HOST>woot.com</HOST>',
                '<LOCALNAMESPACEPATH>',
                '<NAMESPACE NAME="root"/>',
                '<NAMESPACE NAME="cimv2"/>',
                '</LOCALNAMESPACEPATH>',
                '</NAMESPACEPATH>',
                '<CLASSNAME NAME="CIM_Foo"/>',
                '</CLASSPATH>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with namespace+host, with non-default: ignore_host=True",
        dict(
            obj=CIMClassName(
                'CIM_Foo',
                namespace='root/cimv2',
                host='woot.com',
            ),
            kwargs=dict(
                ignore_host=True,
            ),
            exp_xml_str=(
                '<LOCALCLASSPATH>',
                '<LOCALNAMESPACEPATH>',
                '<NAMESPACE NAME="root"/>',
                '<NAMESPACE NAME="cimv2"/>',
                '</LOCALNAMESPACEPATH>',
                '<CLASSNAME NAME="CIM_Foo"/>',
                '</LOCALCLASSPATH>',
            )
        ),
        None, None, True
    ),
    (
        "Classname with namespace+host, with non-def: ignore_namespace=True",
        dict(
            obj=CIMClassName(
                'CIM_Foo',
                namespace='root/cimv2',
                host='woot.com',
            ),
            kwargs=dict(
                ignore_namespace=True,
            ),
            exp_xml_str=(
                '<CLASSNAME NAME="CIM_Foo"/>',
            )
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMCLASSNAME_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMClassName_tocimxml(testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMClassName.tocimxml().
    """

    # The code to be tested
    obj_xml = obj.tocimxml(**kwargs)

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

    obj_xml_str = obj_xml.toxml()
    exp_xml_str = ''.join(exp_xml_str)
    validate_cim_xml_obj(obj, obj_xml_str, exp_xml_str)


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMCLASSNAME_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMClassName_tocimxmlstr(testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMClassName.tocimxmlstr().
    """

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(**kwargs)

    # 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(obj_xml_str, str)

    exp_xml_str = ''.join(exp_xml_str)
    validate_cim_xml_obj(obj, obj_xml_str, exp_xml_str)


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMCLASSNAME_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMClassName_tocimxmlstr_indent_int(
        testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMClassName.tocimxmlstr() with indent as integer.
    """

    indent = 4
    indent_str = ' ' * indent

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(indent=indent, **kwargs)

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

    obj_xml = obj.tocimxml(**kwargs)  # This is tested elsewhere
    exp_xml_str = obj_xml.toprettyxml(indent=indent_str)

    assert obj_xml_str == exp_xml_str, (
        f"{obj.__class__.__name__}.tocimxmlstr(indent={indent!r}) "
        "returns unexpected CIM-XML string")


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMCLASSNAME_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMClassName_tocimxmlstr_indent_str(
        testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMClassName.tocimxmlstr() with indent as string.
    """

    indent = 4
    indent_str = ' ' * indent

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(indent=indent_str, **kwargs)

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

    obj_xml = obj.tocimxml(**kwargs)  # This is tested elsewhere
    exp_xml_str = obj_xml.toprettyxml(indent=indent_str)

    assert obj_xml_str == exp_xml_str, (
        f"{obj.__class__.__name__}.tocimxmlstr(indent={indent_str!r}) "
        "returns unexpected CIM-XML string")


TESTCASES_CIMCLASSNAME_FROM_WBEM_URI = [

    # Testcases for CIMClassName.from_wbem_uri()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * uri: WBEM URI string to be tested.
    #   * exp_attrs: Dict of all expected attributes of created object, 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

    (
        "all components, normal case",
        dict(
            uri='https://10.11.12.13:5989/root/cimv2:CIM_Foo',
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
        ),
        None, None, True
    ),
    (
        "no authority",
        dict(
            uri='https:/root/cimv2:CIM_Foo',
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host=None),
        ),
        None, None, True
    ),
    (
        "authority with user:password",
        dict(
            uri='https://jdd:test@10.11.12.13:5989/root/cimv2:CIM_Foo',
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='jdd:test@10.11.12.13:5989'),
        ),
        None, None, True
    ),
    (
        "authority with user (no password)",
        dict(
            uri='https://jdd@10.11.12.13:5989/root/cimv2:CIM_Foo',
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='jdd@10.11.12.13:5989'),
        ),
        None, None, True
    ),
    (
        "authority without port",
        dict(
            uri='https://10.11.12.13/root/cimv2:CIM_Foo',
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='10.11.12.13'),
        ),
        None, None, True
    ),
    (
        "authority with IPv6 address",
        dict(
            uri='https://[10:11:12:13]/root/cimv2:CIM_Foo',
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='[10:11:12:13]'),
        ),
        None, None, True
    ),
    (
        "authority with IPv6 address and port",
        dict(
            uri='https://[10:11:12:13]:5989/root/cimv2:CIM_Foo',
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='[10:11:12:13]:5989'),
        ),
        None, None, True
    ),
    (
        "no namespace type",
        dict(
            uri='//10.11.12.13:5989/root/cimv2:CIM_Foo',
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
        ),
        None, None, True
    ),
    (
        "namespace type http",
        dict(
            uri='http://10.11.12.13:5989/root/cimv2:CIM_Foo',
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
        ),
        None, None, True
    ),
    (
        "namespace type upper case HTTP",
        dict(
            uri='HTTP://10.11.12.13:5989/root/cimv2:CIM_Foo',
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
        ),
        None, None, True
    ),
    (
        "namespace type mixed case HttpS",
        dict(
            uri='HttpS://10.11.12.13:5989/root/cimv2:CIM_Foo',
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
        ),
        None, None, True
    ),
    (
        "namespace type cimxml-wbem",
        dict(
            uri='cimxml-wbem://10.11.12.13:5989/root/cimv2:CIM_Foo',
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
        ),
        None, None, True
    ),
    (
        "namespace type cimxml-wbems",
        dict(
            uri='cimxml-wbems://10.11.12.13:5989/root/cimv2:CIM_Foo',
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
        ),
        None, None, True
    ),
    (
        "unknown namespace type",
        dict(
            uri='xyz://10.11.12.13:5989/root/cimv2:CIM_Foo',
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
        ),
        None, UserWarning, True
    ),
    (
        "local WBEM URI (no namespace type, no authority)",
        dict(
            uri='/root/cimv2:CIM_Foo',
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host=None),
        ),
        None, None, True
    ),
    (
        "local WBEM URI (with missing initial slash)",
        dict(
            uri='root/cimv2:CIM_Foo',
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host=None),
        ),
        None, None, True
    ),
    (
        "local WBEM URI with only class name (with initial slash+colon)",
        dict(
            uri='/:CIM_Foo',
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace=None,
                host=None),
        ),
        None, None, True
    ),
    (
        "local WBEM URI with only class name (without initial slash)",
        dict(
            uri=':CIM_Foo',
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace=None,
                host=None),
        ),
        None, None, True
    ),
    (
        "local WBEM URI with only class name (without initial slash+colon)",
        dict(
            uri='CIM_Foo',
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace=None,
                host=None),
        ),
        None, None, True
    ),
    (
        "local WBEM URI with namespace that has only one component",
        dict(
            uri='/root:CIM_Foo',
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root',
                host=None),
        ),
        None, None, True
    ),
    (
        "local WBEM URI with namespace that has three components",
        dict(
            uri='/root/cimv2/test:CIM_Foo',
            exp_attrs=dict(
                classname='CIM_Foo',
                namespace='root/cimv2/test',
                host=None),
        ),
        None, None, True
    ),
    (
        "missing delimiter / before authority",
        dict(
            uri='https:/10.11.12.13/cimv2:CIM_Foo',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "invalid char ; in authority",
        dict(
            uri='https://10.11.12.13;5989/cimv2:CIM_Foo',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "delimiter / before namespace replaced with :",
        dict(
            uri='https://10.11.12.13:5989:root:CIM_Foo',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "delimiter : before classname replaced with .",
        dict(
            uri='https://10.11.12.13:5989/root.CIM_Foo',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "invalid '/' between namespace and classname",
        dict(
            uri='https://10.11.12.13:5989/cimv2/CIM_Foo',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "class name missing",
        dict(
            uri='https://10.11.12.13:5989/root/cimv2',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
    (
        "instance path used as class path",
        dict(
            uri='https://10.11.12.13:5989/root:CIM_Foo.k1="v1"',
            exp_attrs=None,
        ),
        ValueError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMCLASSNAME_FROM_WBEM_URI)
@simplified_test_function
@log_entry_exit
def test_CIMClassName_from_wbem_uri(testcase, uri, exp_attrs):
    """
    Test function for CIMClassName.from_wbem_uri()
    """

    # The code to be tested
    obj = CIMClassName.from_wbem_uri(uri)

    # 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(obj, CIMClassName)

    exp_classname = exp_attrs['classname']
    assert obj.classname == exp_classname
    assert isinstance(obj.classname, type(exp_classname))

    exp_namespace = exp_attrs['namespace']
    assert obj.namespace == exp_namespace
    assert isinstance(obj.namespace, type(exp_namespace))

    exp_host = exp_attrs['host']
    assert obj.host == exp_host
    assert isinstance(obj.host, type(exp_host))


TESTCASES_CIMCLASSNAME_TO_WBEM_URI = [

    # Testcases for CIMClassName.to_wbem_uri()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: CIMClassName object to be tested.
    #   * kwargs: Keyword arguments for to_wbem_uri():
    #     * format: Format for to_wbem_uri(): one of 'standard', 'canonical',
    #       'cimobject', 'historical'.
    #   * exp_result: Expected WBEM URI string, or None if failed.
    # * 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

    (
        "all components, standard format",
        dict(
            obj=CIMClassName(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='//10.11.12.13:5989/root/cimv2:CIM_Foo',
        ),
        None, None, True
    ),
    (
        "all components, cimobject format (host is ignored)",
        dict(
            obj=CIMClassName(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
            kwargs=dict(
                format='cimobject',
            ),
            exp_uri='/root/cimv2:CIM_Foo',
        ),
        None, None, True
    ),
    (
        "all components, historical format",
        dict(
            obj=CIMClassName(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
            kwargs=dict(
                format='historical',
            ),
            exp_uri='//10.11.12.13:5989/root/cimv2:CIM_Foo',
        ),
        None, None, True
    ),
    (
        "all components, canonical format",
        dict(
            obj=CIMClassName(
                classname='CIM_Foo',
                namespace='Root/CIMv2',
                host='MyServer:5989'),
            kwargs=dict(
                format='canonical',
            ),
            exp_uri='//myserver:5989/root/cimv2:cim_foo',
        ),
        None, None, True
    ),
    (
        "all components, invalid format",
        dict(
            obj=CIMClassName(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
            kwargs=dict(
                format='xxx_invalid_format',
            ),
            exp_uri=None,
        ),
        ValueError, None, True
    ),
    (
        "no authority, standard format",
        dict(
            obj=CIMClassName(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/root/cimv2:CIM_Foo',
        ),
        None, None, True
    ),
    (
        "no authority, cimobject format",
        dict(
            obj=CIMClassName(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host=None),
            kwargs=dict(
                format='cimobject',
            ),
            exp_uri='root/cimv2:CIM_Foo',
        ),
        None, None, True
    ),
    (
        "no authority, historical format",
        dict(
            obj=CIMClassName(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host=None),
            kwargs=dict(
                format='historical',
            ),
            exp_uri='root/cimv2:CIM_Foo',
        ),
        None, None, True
    ),
    (
        "authority with user:password",
        dict(
            obj=CIMClassName(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='jdd:test@10.11.12.13:5989'),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='//jdd:test@10.11.12.13:5989/root/cimv2:CIM_Foo',
        ),
        None, None, True
    ),
    (
        "authority with user (no password)",
        dict(
            obj=CIMClassName(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='jdd@10.11.12.13:5989'),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='//jdd@10.11.12.13:5989/root/cimv2:CIM_Foo',
        ),
        None, None, True
    ),
    (
        "authority without port",
        dict(
            obj=CIMClassName(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='10.11.12.13'),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='//10.11.12.13/root/cimv2:CIM_Foo',
        ),
        None, None, True
    ),
    (
        "authority with IPv6 address",
        dict(
            obj=CIMClassName(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='[10:11:12:13]'),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='//[10:11:12:13]/root/cimv2:CIM_Foo',
        ),
        None, None, True
    ),
    (
        "authority with IPv6 address and port",
        dict(
            obj=CIMClassName(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='[10:11:12:13]:5989'),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='//[10:11:12:13]:5989/root/cimv2:CIM_Foo',
        ),
        None, None, True
    ),
    (
        "local WBEM URI (no authority)",
        dict(
            obj=CIMClassName(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/root/cimv2:CIM_Foo',
        ),
        None, None, True
    ),
    (
        "local WBEM URI with only class name, standard format",
        dict(
            obj=CIMClassName(
                classname='CIM_Foo',
                namespace=None,
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/:CIM_Foo',
        ),
        None, None, True
    ),
    (
        "local WBEM URI with only class name, cimobject format",
        dict(
            obj=CIMClassName(
                classname='CIM_Foo',
                namespace=None,
                host=None),
            kwargs=dict(
                format='cimobject',
            ),
            exp_uri=':CIM_Foo',
        ),
        None, None, True
    ),
    (
        "local WBEM URI with only class name, historical format",
        dict(
            obj=CIMClassName(
                classname='CIM_Foo',
                namespace=None,
                host=None),
            kwargs=dict(
                format='historical',
            ),
            exp_uri='CIM_Foo',
        ),
        None, None, True
    ),
    (
        "local WBEM URI with namespace that has only one component",
        dict(
            obj=CIMClassName(
                classname='CIM_Foo',
                namespace='root',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/root:CIM_Foo',
        ),
        None, None, True
    ),
    (
        "local WBEM URI with namespace that has three components",
        dict(
            obj=CIMClassName(
                classname='CIM_Foo',
                namespace='root/cimv2/test',
                host=None),
            kwargs=dict(
                format='standard',
            ),
            exp_uri='/root/cimv2/test:CIM_Foo',
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMCLASSNAME_TO_WBEM_URI)
@simplified_test_function
@log_entry_exit
def test_CIMClassName_to_wbem_uri(testcase, obj, kwargs, exp_uri):
    """
    Test function for CIMClassName.to_wbem_uri()
    """

    # The code to be tested
    uri = obj.to_wbem_uri(**kwargs)

    # 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(uri, str)
    assert uri == exp_uri


TESTCASES_CIMCLASSNAME_STR = [

    # Testcases for CIMClassName.__str__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * attrs: Dict of all attributes for CIMClassName.
    #   * exp_result: Expected WBEM URI string, or None if failed.
    # * 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

    (
        "all components, historical format",
        dict(
            obj=CIMClassName(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host='10.11.12.13:5989'),
            exp_uri='//10.11.12.13:5989/root/cimv2:CIM_Foo',
        ),
        None, None, True
    ),
    (
        "no authority, historical format",
        dict(
            obj=CIMClassName(
                classname='CIM_Foo',
                namespace='root/cimv2',
                host=None),
            exp_uri='root/cimv2:CIM_Foo',
        ),
        None, None, True
    ),
    (
        "local WBEM URI with only class name, historical format",
        dict(
            obj=CIMClassName(
                classname='CIM_Foo',
                namespace=None,
                host=None),
            exp_uri='CIM_Foo',
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMCLASSNAME_STR)
@simplified_test_function
@log_entry_exit
def test_CIMClassName_str(testcase, obj, exp_uri):
    """
    Test function for CIMClassName.__str__()
    """

    # The code to be tested
    uri = obj.__str__()  # pylint: disable=unnecessary-dunder-call

    # 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(uri, str)
    assert uri == exp_uri


TESTCASES_CIMCLASS_INIT = [

    # Testcases for CIMClass.__init__()

    # 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 CIMClass().
    #   * init_kwargs: Dict of keyword arguments to CIMClass().
    #   * exp_attrs: Dict of expected attributes of resulting 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

    # Order of positional arguments
    (
        "Verify order of positional arguments",
        dict(
            init_args=[
                'CIM_Foo',
                [
                    CIMProperty('Prop1', value=True),
                ],
                [
                    CIMMethod('Meth1', return_type='string'),
                ],
                'CIM_FooParent',
                [
                    CIMQualifier('Qual1', value=True),
                ],
                CIMClassName('CIM_Foo'),
            ],
            init_kwargs={},
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('Prop1', CIMProperty('Prop1', value=True)),
                ]),
                methods=NocaseDict([
                    ('Meth1', CIMMethod('Meth1', return_type='string')),
                ]),
                superclass='CIM_FooParent',
                qualifiers=NocaseDict([
                    ('Qual1', CIMQualifier('Qual1', value=True)),
                ]),
                path=CIMClassName('CIM_Foo'),
            )
        ),
        None, None, True
    ),

    # Classname tests
    (
        "Verify that bytes classname is converted to unicode",
        dict(
            init_args=[],
            init_kwargs=dict(classname=b'CIM_Foo'),
            exp_attrs=dict(classname='CIM_Foo')
        ),
        None, None, True
    ),
    (
        "Verify that unicode classname remains unicode",
        dict(
            init_args=[],
            init_kwargs=dict(classname='CIM_Foo'),
            exp_attrs=dict(classname='CIM_Foo')
        ),
        None, None, True
    ),

    # Superclass tests
    (
        "Verify that bytes superclass is converted to unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                superclass=b'CIM_Foo'
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                superclass='CIM_Foo'
            )
        ),
        None, None, True
    ),
    (
        "Verify that unicode superclass remains unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                superclass='CIM_Foo'
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                superclass='CIM_Foo'
            )
        ),
        None, None, True
    ),

    # Properties tests
    (
        "Verify properties order preservation with list of CIMProperty",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=[
                    CIMProperty('P1', value='Ham'),
                    CIMProperty('P2', value='Cheese'),
                ]
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('P1', CIMProperty('P1', value='Ham')),
                    ('P2', CIMProperty('P2', value='Cheese')),
                ])
            )
        ),
        None, None, True
    ),
    (
        "Verify properties order preservation with OrderedDict",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=OrderedDict([
                    ('P1', CIMProperty('P1', value='Ham')),
                    ('P2', CIMProperty('P2', value='Cheese')),
                ])
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('P1', CIMProperty('P1', value='Ham')),
                    ('P2', CIMProperty('P2', value='Cheese')),
                ])
            )
        ),
        None, None, True
    ),
    (
        "Verify properties order preservation with list of tuple(key,val)",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=[
                    ('P1', CIMProperty('P1', value='Ham')),
                    ('P2', CIMProperty('P2', value='Cheese')),
                ]
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict([
                    ('P1', CIMProperty('P1', value='Ham')),
                    ('P2', CIMProperty('P2', value='Cheese')),
                ])
            )
        ),
        None, None, True
    ),
    (
        "Verify that properties dict is converted to NocaseDict",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(P1=CIMPROPERTY_P1_OBJ)
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                properties=NocaseDict(P1=CIMPROPERTY_P1_OBJ)
            )
        ),
        None, None, True
    ),

    # Methods tests
    (
        "Verify methods order preservation with list of CIMMethod",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                methods=[
                    CIMMethod('M1', return_type='string'),
                    CIMMethod('M2', return_type='uint32'),
                ]
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                methods=NocaseDict([
                    ('M1', CIMMethod('M1', return_type='string')),
                    ('M2', CIMMethod('M2', return_type='uint32')),
                ])
            )
        ),
        None, None, True
    ),
    (
        "Verify methods order preservation with OrderedDict",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                methods=OrderedDict([
                    ('M1', CIMMethod('M1', return_type='string')),
                    ('M2', CIMMethod('M2', return_type='uint32')),
                ])
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                methods=NocaseDict([
                    ('M1', CIMMethod('M1', return_type='string')),
                    ('M2', CIMMethod('M2', return_type='uint32')),
                ])
            )
        ),
        None, None, True
    ),
    (
        "Verify methods order preservation with list of tuple(key,val)",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                methods=[
                    ('M1', CIMMethod('M1', return_type='string')),
                    ('M2', CIMMethod('M2', return_type='uint32')),
                ]
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                methods=NocaseDict([
                    ('M1', CIMMethod('M1', return_type='string')),
                    ('M2', CIMMethod('M2', return_type='uint32')),
                ])
            )
        ),
        None, None, True
    ),
    (
        "Verify that methods dict is converted to NocaseDict",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                methods=dict(M1=CIMMETHOD_M1_OBJ)
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                methods=NocaseDict(M1=CIMMETHOD_M1_OBJ)
            )
        ),
        None, None, True
    ),

    # Qualifiers tests
    (
        "Verify qualifiers order preservation with list of CIMQualifier",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                qualifiers=[
                    CIMQualifier('Q1', value='Ham'),
                    CIMQualifier('Q2', value='Cheese'),
                ]
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                qualifiers=NocaseDict([
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                    ('Q2', CIMQualifier('Q2', value='Cheese')),
                ])
            )
        ),
        None, None, True
    ),
    (
        "Verify qualifiers order preservation with OrderedDict",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                qualifiers=OrderedDict([
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                    ('Q2', CIMQualifier('Q2', value='Cheese')),
                ])
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                qualifiers=NocaseDict([
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                    ('Q2', CIMQualifier('Q2', value='Cheese')),
                ])
            )
        ),
        None, None, True
    ),
    (
        "Verify qualifiers order preservation with list of tuple(key,val)",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                qualifiers=[
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                    ('Q2', CIMQualifier('Q2', value='Cheese')),
                ]
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                qualifiers=NocaseDict([
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                    ('Q2', CIMQualifier('Q2', value='Cheese')),
                ])
            )
        ),
        None, None, True
    ),
    (
        "Verify that qualifiers dict is converted to NocaseDict",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                qualifiers=dict(Q1=CIMQUALIFIER_Q1_OBJ)
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                qualifiers=NocaseDict(Q1=CIMQUALIFIER_Q1_OBJ)
            )
        ),
        None, None, True
    ),
    (
        "Verify that qualifier provided as simple value is converted to "
        "CIMQualifier (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                qualifiers=dict(Q1=CIMQUALIFIER_Q1_OBJ.value)
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                qualifiers=NocaseDict(Q1=CIMQUALIFIER_Q1_OBJ)
            )
        ),
        None, None, True
    ),

    # Path tests
    (
        "Verify that CIMClassName path is accepted",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                path=CIMCLASSNAME_C1_OBJ
            ),
            exp_attrs=dict(
                classname='CIM_Foo',
                path=CIMCLASSNAME_C1_OBJ
            )
        ),
        None, None, True
    ),

    # Exception testcases
    (
        "Verify that classname None fails (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(classname=None),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that properties with item of invalid type fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=[
                    'xxx_invalid_type'
                ]
            ),
            exp_attrs=None
        ),
        TypeError, None, True
    ),
    (
        "Verify that property with key None fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties={None: CIMPROPERTY_P1_OBJ}
            ),
            exp_attrs=None
        ),
        # raises ValueError instead of TypeError since 0.12
        ValueError, None, True
    ),
    (
        "Verify that property with inconsistent key / name fails "
        "(since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(P1_X=CIMPROPERTY_P1_OBJ)
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that property provided as simple value fails "
        "(since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                properties=dict(P1=CIMPROPERTY_P1_OBJ.value)
            ),
            exp_attrs=None
        ),
        TypeError, None, True
    ),
    (
        "Verify that methods with item of invalid type fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                methods=[
                    'xxx_invalid_type'
                ]
            ),
            exp_attrs=None
        ),
        TypeError, None, True
    ),
    (
        "Verify that method with key None fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                methods={None: CIMMETHOD_M1_OBJ}
            ),
            exp_attrs=None
        ),
        # raises ValueError instead of TypeError since 0.12
        ValueError, None, True
    ),
    (
        "Verify that method with inconsistent key / name fails "
        "(since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                methods=dict(M1_X=CIMMETHOD_M1_OBJ)
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that method with invalid type CIMParameter fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                methods=[
                    CIMParameter('P1', type='string'),
                ]
            ),
            exp_attrs=None
        ),
        TypeError, None, True
    ),
    (
        "Verify that method with invalid type string fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                methods=[
                    'invalid',
                ]
            ),
            exp_attrs=None
        ),
        TypeError, None, True
    ),
    (
        "Verify that qualifiers with item of invalid type fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                qualifiers=[
                    'xxx_invalid_type'
                ]
            ),
            exp_attrs=None
        ),
        TypeError, None, True
    ),
    (
        "Verify that qualifier with key None fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                qualifiers={None: CIMQUALIFIER_Q1_OBJ}
            ),
            exp_attrs=None
        ),
        # raises ValueError instead of TypeError since 0.12
        ValueError, None, True
    ),
    (
        "Verify that qualifier with inconsistent key / name fails "
        "(since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                classname='CIM_Foo',
                qualifiers=dict(Q1_X=CIMQUALIFIER_Q1_OBJ)
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMCLASS_INIT)
@simplified_test_function
@log_entry_exit
def test_CIMClass_init(testcase, init_args, init_kwargs, exp_attrs):
    """
    Test function for CIMClass.__init__()
    """

    # The code to be tested
    obj = CIMClass(*init_args, **init_kwargs)

    assert not hasattr(obj, '__dict__')

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

    exp_classname = exp_attrs['classname']
    assert obj.classname == exp_classname
    assert isinstance(obj.classname, type(exp_classname))

    exp_properties = exp_attrs.get('properties', NocaseDict())
    assert obj.properties == exp_properties
    assert isinstance(obj.properties, type(exp_properties))

    exp_methods = exp_attrs.get('methods', NocaseDict())
    assert obj.methods == exp_methods
    assert isinstance(obj.methods, type(exp_methods))

    exp_superclass = exp_attrs.get('superclass', None)
    assert obj.superclass == exp_superclass
    assert isinstance(obj.superclass, type(exp_superclass))

    exp_qualifiers = exp_attrs.get('qualifiers', NocaseDict())
    assert obj.qualifiers == exp_qualifiers
    assert isinstance(obj.qualifiers, type(exp_qualifiers))

    exp_path = exp_attrs.get('path', None)
    assert obj.path == exp_path
    assert isinstance(obj.path, type(exp_path))


TESTCASES_CIMCLASS_COPY = [

    # Testcases for CIMClass.copy()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj_kwargs: Dict of init kwargs for original CIMClass.
    # * 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

    (
        "All attributes set",
        dict(
            obj_kwargs=dict(
                classname='CIM_Foo',
                superclass='CIM_FooParent',
                properties=[
                    CIMProperty('Name', value='Foo'),
                ],
                methods=[
                    CIMMethod('Method1', return_type='string'),
                ],
                qualifiers=[
                    CIMQualifier('Key', value=True),
                ],
                path=CIMClassName('CIM_Foo'),
            )
        ),
        None, None, True
    ),
    (
        "No Superclass",
        dict(
            obj_kwargs=dict(
                classname='CIM_Foo',
                superclass=None,
                properties=[
                    CIMProperty('Name', value='Foo'),
                ],
                methods=[
                    CIMMethod('Method1', return_type='string'),
                ],
                qualifiers=[
                    CIMQualifier('Key', value=True),
                ],
                path=CIMClassName('CIM_Foo'),
            )
        ),
        None, None, True
    ),
    (
        "No Properties",
        dict(
            obj_kwargs=dict(
                classname='CIM_Foo',
                superclass='CIM_FooParent',
                properties=None,
                methods=[
                    CIMMethod('Method1', return_type='string'),
                ],
                qualifiers=[
                    CIMQualifier('Key', value=True),
                ],
                path=CIMClassName('CIM_Foo'),
            )
        ),
        None, None, True
    ),
    (
        "No Methods",
        dict(
            obj_kwargs=dict(
                classname='CIM_Foo',
                superclass='CIM_FooParent',
                properties=[
                    CIMProperty('Name', value='Foo'),
                ],
                methods=None,
                qualifiers=[
                    CIMQualifier('Key', value=True),
                ],
                path=CIMClassName('CIM_Foo'),
            )
        ),
        None, None, True
    ),
    (
        "No Qualifiers",
        dict(
            obj_kwargs=dict(
                classname='CIM_Foo',
                superclass='CIM_FooParent',
                properties=[
                    CIMProperty('Name', value='Foo'),
                ],
                methods=[
                    CIMMethod('Method1', return_type='string'),
                ],
                qualifiers=None,
                path=CIMClassName('CIM_Foo'),
            )
        ),
        None, None, True
    ),
    (
        "No Path",
        dict(
            obj_kwargs=dict(
                classname='CIM_Foo',
                superclass='CIM_FooParent',
                properties=[
                    CIMProperty('Name', value='Foo'),
                ],
                methods=[
                    CIMMethod('Method1', return_type='string'),
                ],
                qualifiers=[
                    CIMQualifier('Key', value=True),
                ],
                path=None
            )
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMCLASS_COPY)
@simplified_test_function
@log_entry_exit
def test_CIMClass_copy(testcase, obj_kwargs):
    """
    Test function for CIMClass.copy()
    """

    obj1 = CIMClass(**obj_kwargs)

    # The code to be tested
    obj2 = obj1.copy()

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

    # Verify that the copy is equal to the original (performs deep equality)
    assert obj2 == obj1

    # Verify that the copy is a different object
    assert id(obj2) != id(obj1)

    # Verify that the mutable child objects are different objects
    if obj1.properties is not None:
        assert id(obj2.properties) != id(obj1.properties)
    if obj1.methods is not None:
        assert id(obj2.methods) != id(obj1.methods)
    if obj1.qualifiers is not None:
        assert id(obj2.qualifiers) != id(obj1.qualifiers)
    if obj1.path is not None:
        assert id(obj2.path) != id(obj1.path)

    # Verify that the copy can be modified and the original remains unchanged

    obj1_classname = obj1.classname
    obj2.classname = 'SomeNewClassname'
    assert obj1.classname == obj1_classname

    obj1_superclass = obj1.superclass
    obj2.superclass = 'SomeNewClassname'
    assert obj1.superclass == obj1_superclass

    obj1_properties = obj1.properties
    obj2.properties = [CIMProperty('SomeNewProperty', value='somevalue')]
    assert obj1.properties == obj1_properties

    obj1_methods = obj1.methods
    obj2.methods = [CIMMethod('SomeNewMethod', return_type='string')]
    assert obj1.methods == obj1_methods

    obj1_qualifiers = obj1.qualifiers
    obj2.qualifiers = [CIMQualifier('SomeNewQualifier', value=True)]
    assert obj1.qualifiers == obj1_qualifiers

    obj1_path = obj1.path
    obj2.path = CIMClassName('SomeNewClassname')
    assert obj1.path == obj1_path


CIMCLASS_SETATTR_C1_KWARGS = dict(
    classname='C1',
    superclass='CP',
    properties=dict(P1=CIMProperty('P1', False)),
    methods=dict(M1=CIMMethod('M1', 'boolean')),
    qualifiers=dict(Q1=CIMQualifier('Q1', False)),
    path=CIMClassName('C1', dict(P1=False)),
)

TESTCASES_CIMCLASS_SETATTR = [

    # Testcases for CIMClass set attribute

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj_kwargs: Dict of init kwargs for CIMClass.
    #   * item: Name of CIMClass attr to set, or tuple
    #     (item, key) to set a single item in an attr that is a dict.
    #   * new_value: New value to set.
    #   * exp_attrs: Dict of expected attributes, for validation.
    # * 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

    # Tests that set the classname attribute
    (
        "Set classname to different string",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item='classname',
            new_value='CIM_Bar',
            exp_attrs=dict(
                classname='CIM_Bar',
            ),
        ),
        None, None, True
    ),
    (
        "Set classname to None",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item='classname',
            new_value=None,
            exp_attrs=None,
        ),
        # raises ValueError since 0.12
        ValueError, None, True
    ),

    # Tests that set the superclass attribute
    (
        "Set superclass from class name to different class name",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item='superclass',
            new_value='CP2',
            exp_attrs=dict(
                superclass='CP2',
            ),
        ),
        None, None, True
    ),
    (
        "Set superclass from class name to None",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item='superclass',
            new_value=None,
            exp_attrs=dict(
                superclass=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the properties attribute
    (
        "Set properties to new dict with invalid type instead of CIMProperty",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item='properties',
            new_value=dict(P2='xxx_invalid_type'),
            exp_attrs=None,
        ),
        TypeError, None, True
    ),
    (
        "Set properties to new dict with CIMProperty with correct name",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item='properties',
            new_value=dict(P2=CIMProperty('P2', True)),
            exp_attrs=dict(
                properties=dict(
                    P2=CIMProperty('P2', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set properties to new dict with CIMProperty with incorrect name",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item='properties',
            new_value=dict(P2=CIMProperty('P2x', True)),
            exp_attrs=None,
        ),
        # raises ValueError since 0.12
        ValueError, None, True
    ),
    (
        "Set properties to new dict with CIMProperty with name None",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item='properties',
            # Name of CIMProperty object is not None, to get over that check.
            new_value=dict([(None, CIMProperty('Pnone', True))]),
            exp_attrs=dict(
                properties=dict([
                    ('P1', CIMProperty('P1', False)),
                    (None, CIMProperty('Pnone', True)),
                ]),
            ),
        ),
        # raises ValueError since 0.12
        ValueError, None, True
    ),
    (
        "Set properties to None",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item='properties',
            new_value=None,
            exp_attrs=dict(
                properties={},
            ),
        ),
        None, None, True
    ),
    (
        "Set existing property to new CIMProperty with correct name",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item=('properties', 'P1'),
            new_value=CIMProperty('P1', True),
            exp_attrs=dict(
                properties=dict(
                    P1=CIMProperty('P1', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set existing property to new CIMProperty with incorrect name",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item=('properties', 'P1'),
            new_value=CIMProperty('P1x', True),
            # No checking for correct name since this sets an item in a normal
            # NocaseDict:
            exp_attrs=dict(
                properties=dict(
                    P1=CIMProperty('P1x', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new property to new CIMProperty with correct name",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item=('properties', 'P2'),
            new_value=CIMProperty('P2', True),
            exp_attrs=dict(
                properties=dict(
                    P1=CIMProperty('P1', False),
                    P2=CIMProperty('P2', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new property to new CIMProperty with incorrect name",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item=('properties', 'P2'),
            new_value=CIMProperty('P2x', True),
            # No checking for correct name since this sets an item in a normal
            # NocaseDict:
            exp_attrs=dict(
                properties=dict(
                    P1=CIMProperty('P1', False),
                    P2=CIMProperty('P2x', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new property with name None",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item=('properties', None),
            # Name of CIMProperty object is not None, to get over that check.
            new_value=CIMProperty('Pnone', True),
            exp_attrs=None,
        ),
        ValueError if version_info >= (1, 0) else TypeError, None, True
        # None as key in NocaseDict
    ),

    # Tests that set the methods attribute
    (
        "Set methods to new dict with invalid type instead of CIMMethod",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item='methods',
            new_value=dict(M2='xxx_invalid_type'),
            exp_attrs=None,
        ),
        TypeError, None, True
    ),
    (
        "Set methods to new dict with CIMMethod with correct name",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item='methods',
            new_value=dict(M2=CIMMethod('M2', 'boolean')),
            exp_attrs=dict(
                methods=dict(
                    M2=CIMMethod('M2', 'boolean'),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set methods to new dict with CIMMethod with incorrect name",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item='methods',
            new_value=dict(M2=CIMMethod('M2x', 'boolean')),
            exp_attrs=None,
        ),
        # raises ValueError since 0.12
        ValueError, None, True
    ),
    (
        "Set methods to new dict with CIMMethod with name None",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item='methods',
            # Name of CIMMethod object is not None, to get over that check.
            new_value=dict([(None, CIMMethod('Pnone', 'boolean'))]),
            exp_attrs=dict(
                properties=dict([
                    ('M1', CIMMethod('M1', 'boolean')),
                    (None, CIMMethod('Pnone', 'boolean')),
                ]),
            ),
        ),
        # raises ValueError since 0.12
        ValueError, None, True
    ),
    (
        "Set methods to None",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item='methods',
            new_value=None,
            exp_attrs=dict(
                methods={},
            ),
        ),
        None, None, True
    ),
    (
        "Set existing method to new CIMMethod with correct name",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item=('methods', 'M1'),
            new_value=CIMMethod('M1', 'boolean'),
            exp_attrs=dict(
                methods=dict(
                    M1=CIMMethod('M1', 'boolean'),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set existing method to new CIMMethod with incorrect name",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item=('methods', 'M1'),
            new_value=CIMMethod('M1x', 'boolean'),
            # No checking for correct name since this sets an item in a normal
            # NocaseDict:
            exp_attrs=dict(
                methods=dict(
                    M1=CIMMethod('M1x', 'boolean'),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new method to new CIMMethod with correct name",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item=('methods', 'M2'),
            new_value=CIMMethod('M2', 'boolean'),
            exp_attrs=dict(
                methods=dict(
                    M1=CIMMethod('M1', 'boolean'),
                    M2=CIMMethod('M2', 'boolean'),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new method to new CIMMethod with incorrect name",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item=('methods', 'M2'),
            new_value=CIMMethod('M2x', 'boolean'),
            # No checking for correct name since this sets an item in a normal
            # NocaseDict:
            exp_attrs=dict(
                methods=dict(
                    M1=CIMMethod('M1', 'boolean'),
                    M2=CIMMethod('M2x', 'boolean'),
                ),
            ),
        ),
        None, None, True
    ),

    # Tests that set the qualifiers attribute
    (
        "Set qualifiers to new dict with CIMQualifier with correct name",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item='qualifiers',
            new_value=dict(Q2=CIMQualifier('Q2', True)),
            exp_attrs=dict(
                qualifiers=dict(
                    Q2=CIMQualifier('Q2', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set qualifiers to new dict with CIMQualifier with incorrect name",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item='qualifiers',
            new_value=dict(Q2=CIMQualifier('Q2x', True)),
            exp_attrs=None,
        ),
        # raises ValueError since 0.12
        ValueError, None, True
    ),
    (
        "Set qualifiers to new dict with CIMQualifier with name None",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item='qualifiers',
            # Name of CIMQualifier object is not None, to get over that check.
            new_value=dict([(None, CIMQualifier('Qnone', True))]),
            exp_attrs=dict(
                qualifiers=dict([
                    ('Q1', CIMQualifier('Q1', False)),
                    (None, CIMQualifier('Qnone', True)),
                ]),
            ),
        ),
        # raises ValueError since 0.12
        ValueError, None, True
    ),
    (
        "Set qualifiers to new dict with simple value",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item='qualifiers',
            new_value=dict(Q1=True),
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1', True)
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set qualifiers to None",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item='qualifiers',
            new_value=None,
            exp_attrs=dict(
                qualifiers={},
            ),
        ),
        None, None, True
    ),
    (
        "Set existing qualifier to new CIMQualifier with correct name",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item=('qualifiers', 'Q1'),
            new_value=CIMQualifier('Q1', True),
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set existing qualifier to new CIMQualifier with incorrect name",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item=('qualifiers', 'Q1'),
            new_value=CIMQualifier('Q1x', True),
            # No checking for correct name since this sets an item in a normal
            # NocaseDict:
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1x', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new qualifier to new CIMQualifier with correct name",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item=('qualifiers', 'Q2'),
            new_value=CIMQualifier('Q2', True),
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1', False),
                    Q2=CIMQualifier('Q2', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new qualifier to new CIMQualifier with incorrect name",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item=('qualifiers', 'Q2'),
            new_value=CIMQualifier('Q2x', True),
            # No checking for correct name since this sets an item in a normal
            # NocaseDict:
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1', False),
                    Q2=CIMQualifier('Q2x', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new qualifier with name None",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item=('qualifiers', None),
            # Name of CIMQualifier object is not None, to get over that check.
            new_value=CIMQualifier('Qnone', True),
            exp_attrs=None,
        ),
        ValueError if version_info >= (1, 0) else TypeError, None, True
        # None as key in NocaseDict
    ),

    # Tests that set the path attribute
    (
        "Set path to new CIMClassName",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item='path',
            new_value=CIMCLASSNAME_C1_OBJ,
            exp_attrs=dict(
                path=CIMCLASSNAME_C1_OBJ,
            ),
        ),
        None, None, True
    ),
    (
        "Set path to None",
        dict(
            obj_kwargs=CIMCLASS_SETATTR_C1_KWARGS,
            item='path',
            new_value=None,
            exp_attrs=dict(
                path=None,
            ),
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMCLASS_SETATTR)
@simplified_test_function
@log_entry_exit
def test_CIMClass_setattr(
        testcase, obj_kwargs, item, new_value, exp_attrs):
    """
    Test function for CIMClass set attribute
    """

    obj = CIMClass(**obj_kwargs)

    if isinstance(item, tuple):
        attr_name, attr_key = item
        attr_dict = getattr(obj, attr_name)

        # The code to be tested (for setting a single dict item)
        attr_dict[attr_key] = new_value

    else:
        attr_name = item

        # The code to be tested (for setting the entire attribute)
        setattr(obj, attr_name, new_value)

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

    for attr_name in exp_attrs:
        exp_value = exp_attrs[attr_name]
        value = getattr(obj, attr_name)
        assert value == exp_value


TESTCASES_CIMCLASS_HASH_EQ = [

    # Testcases for CIMClass.__hash__() and __eq__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: CIMClass object #1 to be tested.
    #   * obj2: CIMClass object #2 to be tested.
    #   * exp_equal: Expected equality of the object hash values.
    # * 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

    # Classname tests
    (
        "Classname, equal with same lexical case",
        dict(
            obj1=CIMClass('CIM_Foo'),
            obj2=CIMClass('CIM_Foo'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Classname, equal with different lexical case",
        dict(
            obj1=CIMClass('CIM_Foo'),
            obj2=CIMClass('ciM_foO'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Classname, different",
        dict(
            obj1=CIMClass('CIM_Foo'),
            obj2=CIMClass('CIM_Foo_x'),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Superclass tests
    (
        "Superclass, equal with same lexical case",
        dict(
            obj1=CIMClass('CIM_Foo', superclass='CIM_Bar'),
            obj2=CIMClass('CIM_Foo', superclass='CIM_Bar'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Superclass, equal with different lexical case",
        dict(
            obj1=CIMClass('CIM_Foo', superclass='CIM_Bar'),
            obj2=CIMClass('CIM_Foo', superclass='ciM_bAR'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Superclass, different",
        dict(
            obj1=CIMClass('CIM_Foo', superclass='CIM_Bar'),
            obj2=CIMClass('CIM_Foo', superclass='CIM_Bar_x'),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Path tests
    (
        "Class paths, equal with same lexical case",
        dict(
            obj1=CIMClass(
                'CIM_Foo',
                path=CIMClassName('CIM_Foo'),
            ),
            obj2=CIMClass(
                'CIM_Foo',
                path=CIMClassName('CIM_Foo'),
            ),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Class paths, equal with different lexical case in classname",
        dict(
            obj1=CIMClass(
                'CIM_Foo',
                path=CIMClassName('CIM_Foo'),
            ),
            obj2=CIMClass(
                'CIM_Foo',
                path=CIMClassName('CIM_foo'),
            ),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Class paths, different",
        dict(
            obj1=CIMClass(
                'CIM_Foo',
                path=CIMClassName('CIM_Foo'),
            ),
            obj2=CIMClass(
                'CIM_Foo',
                path=CIMClassName('CIM_Foo_x'),
            ),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Properties tests
    (
        "Matching properties, names with same lexical case",
        dict(
            obj1=CIMClass('CIM_Foo', properties=[
                CIMProperty('p1', value='v1'),
            ]),
            obj2=CIMClass('CIM_Foo', properties=[
                CIMProperty('p1', value='v1'),
            ]),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Matching properties, names with different lexical case",
        dict(
            obj1=CIMClass('CIM_Foo', properties=[
                CIMProperty('p1', value='v1'),
            ]),
            obj2=CIMClass('CIM_Foo', properties=[
                CIMProperty('P1', value='v1'),
            ]),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Non-matching properties, one property more",
        dict(
            obj1=CIMClass('CIM_Foo', properties=[
                CIMProperty('p1', value='v1'),
            ]),
            obj2=CIMClass('CIM_Foo', properties=[
                CIMProperty('p1', value='v1'),
                CIMProperty('p2', value='v2'),
            ]),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching properties, one property less",
        dict(
            obj1=CIMClass('CIM_Foo', properties=[
                CIMProperty('p1', value='v1'),
                CIMProperty('p2', value='v2'),
            ]),
            obj2=CIMClass('CIM_Foo', properties=[
                CIMProperty('p1', value='v1'),
            ]),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching properties, different properties",
        dict(
            obj1=CIMClass('CIM_Foo', properties=[
                CIMProperty('p2', value='v2'),
            ]),
            obj2=CIMClass('CIM_Foo', properties=[
                CIMProperty('p1', value='v1'),
            ]),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Matching properties, default values with different lexical case",
        dict(
            obj1=CIMClass('CIM_Foo', properties=[
                CIMProperty('p1', value='v1'),
            ]),
            obj2=CIMClass('CIM_Foo', properties=[
                CIMProperty('p1', value='V1'),
            ]),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Matching properties, with default values as unicode / string",
        dict(
            obj1=CIMClass('CIM_Foo', properties=[
                CIMProperty('p1', value='v1'),
            ]),
            obj2=CIMClass('CIM_Foo', properties=[
                CIMProperty('p1', value='v1'),
            ]),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Matching properties, equal with a number of types",
        dict(
            obj1=CIMClass('CIM_Foo', properties=[
                CIMProperty('pstr', value='v1'),
                CIMProperty('pboo', value=False),
                CIMProperty('pui8', value=Uint8(42)),
                CIMProperty('pref', value=CIMInstanceName('CIM_Bar')),
            ]),
            obj2=CIMClass('CIM_Foo', properties=[
                CIMProperty('pstr', value='v1'),
                CIMProperty('pboo', value=False),
                CIMProperty('pui8', value=Uint8(42)),
                CIMProperty('pref', value=CIMInstanceName('CIM_Bar')),
            ]),
            exp_equal=True,
        ),
        None, None, True
    ),

    # Methods tests
    (
        "Matching methods, names with same lexical case",
        dict(
            obj1=CIMClass('CIM_Foo', methods=[
                CIMMethod('m1', return_type='string'),
            ]),
            obj2=CIMClass('CIM_Foo', methods=[
                CIMMethod('m1', return_type='string'),
            ]),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Matching methods, names with different lexical case",
        dict(
            obj1=CIMClass('CIM_Foo', methods=[
                CIMMethod('m1', return_type='string'),
            ]),
            obj2=CIMClass('CIM_Foo', methods=[
                CIMMethod('M1', return_type='string'),
            ]),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Non-matching methods, one method more",
        dict(
            obj1=CIMClass('CIM_Foo', methods=[
                CIMMethod('m1', return_type='string'),
            ]),
            obj2=CIMClass('CIM_Foo', methods=[
                CIMMethod('m1', return_type='string'),
                CIMMethod('m2', return_type='string'),
            ]),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching methods, one method less",
        dict(
            obj1=CIMClass('CIM_Foo', methods=[
                CIMMethod('m1', return_type='string'),
                CIMMethod('m2', return_type='string'),
            ]),
            obj2=CIMClass('CIM_Foo', methods=[
                CIMMethod('m1', return_type='string'),
            ]),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching methods, different methods",
        dict(
            obj1=CIMClass('CIM_Foo', methods=[
                CIMMethod('m1', return_type='string'),
            ]),
            obj2=CIMClass('CIM_Foo', methods=[
                CIMMethod('m2', return_type='string'),
            ]),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Matching methods, different return types",
        dict(
            obj1=CIMClass('CIM_Foo', methods=[
                CIMMethod('m1', return_type='string'),
            ]),
            obj2=CIMClass('CIM_Foo', methods=[
                CIMMethod('m1', return_type='uint8'),
            ]),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Qualifiers tests
    (
        "Matching qualifiers, qualifier names with same lexical case",
        dict(
            obj1=CIMClass('CIM_Foo',
                          qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMClass('CIM_Foo',
                          qualifiers={'Cheepy': 'Birds'}),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Matching qualifiers, qualifier names with different lexical case",
        dict(
            obj1=CIMClass('CIM_Foo',
                          qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMClass('CIM_Foo',
                          qualifiers={'cheepy': 'Birds'}),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Non-matching qualifiers, one qualifier more",
        dict(
            obj1=CIMClass('CIM_Foo'),
            obj2=CIMClass('CIM_Foo',
                          qualifiers={'Cheepy': 'Birds'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching qualifiers, one qualifier less",
        dict(
            obj1=CIMClass('CIM_Foo',
                          qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMClass('CIM_Foo'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching qualifiers, different qualifiers",
        dict(
            obj1=CIMClass('CIM_Foo',
                          qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMClass('CIM_Foo',
                          qualifiers={'Creepy': 'Ants'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Matching qualifiers, with values that differ in lexical case",
        dict(
            obj1=CIMClass('CIM_Foo',
                          qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMClass('CIM_Foo',
                          qualifiers={'Cheepy': 'birds'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Matching qualifiers, with values that are unicode / string",
        dict(
            obj1=CIMClass('CIM_Foo',
                          qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMClass('CIM_Foo',
                          qualifiers={'Cheepy': 'Birds'}),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Equal qualifiers with a number of types",
        dict(
            obj1=CIMClass(
                'CIM_Foo',
                qualifiers={
                    'Name': 'Foo',
                    'Boolean': False,
                    'Number': Uint8(42),
                }
            ),
            obj2=CIMClass(
                'CIM_Foo',
                qualifiers={
                    'Name': 'Foo',
                    'Boolean': False,
                    'Number': Uint8(42),
                }
            ),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Qualifier with different types: bool True / string 'TRUE'",
        dict(
            obj1=CIMClass('CIM_Foo',
                          qualifiers={'Foo': True}),
            obj2=CIMClass('CIM_Foo',
                          qualifiers={'Foo': 'TRUE'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Qualifier with different types: bool False / string 'FALSE'",
        dict(
            obj1=CIMClass('CIM_Foo',
                          qualifiers={'Foo': False}),
            obj2=CIMClass('CIM_Foo',
                          qualifiers={'Foo': 'FALSE'}),
            exp_equal=False,
        ),
        None, None, True
    ),
]

TESTCASES_CIMCLASS_EQ = [

    # Additional testcases for CIMClass.__eq__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: CIMClass object #1 to be tested.
    #   * obj2: CIMClass object #2 to be tested.
    #   * exp_equal: Expected equality of the object hash values.
    # * 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

    # Classname tests
    (
        "Classname, equal with same lexical case",
        dict(
            obj1=CIMClass('CIM_Foo'),
            obj2='CIM_Foo',
            exp_equal=None,
        ),
        TypeError, None, True
    ),
    (
        "Classname, equal with same lexical case",
        dict(
            obj1=CIMClass('CIM_Foo'),
            obj2=CIMClassName('CIM_Foo'),
            exp_equal=None,
        ),
        TypeError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMCLASS_HASH_EQ)
@simplified_test_function
@log_entry_exit
def test_CIMClass_hash(testcase, obj1, obj2, exp_equal):
    """
    Test function for CIMClass.__hash__().
    """

    # The code to be tested
    hash1 = hash(obj1)
    hash2 = hash(obj2)

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

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

    if exp_equal:
        assert hash1 == hash2
    else:
        assert hash1 != hash2


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMCLASS_HASH_EQ + TESTCASES_CIMCLASS_EQ)
@simplified_test_function
@log_entry_exit
def test_CIMClass_eq(testcase, obj1, obj2, exp_equal):
    """
    Test function for CIMClass.__eq__().
    """

    # The code to be tested
    equal = (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

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

    assert equal == exp_equal


TESTCASES_CIMCLASS_STR_REPR = [

    # Testcases for CIMClass.__repr__(), __str__() / repr(), str()

    # Each list item is a testcase tuple with these items:
    # * obj: CIMClass object to be tested.

    (
        CIMClass(
            classname='CIM_Foo')
    ),
    (
        CIMClass(
            classname='CIM_Foo',
            properties=[
                CIMProperty('Name', 'string'),
                CIMProperty('Ref1', 'reference'),
            ])
    ),
    (
        CIMClass(
            classname='CIM_Foo',
            properties=[
                CIMProperty('Name', 'string'),
                CIMProperty('Ref1', 'reference'),
            ],
            path=CIMClassName('CIM_Foo'))
    ),
]


@pytest.mark.parametrize(
    "obj",
    TESTCASES_CIMCLASS_STR_REPR)
@log_entry_exit
def test_CIMClass_str(obj):
    """
    Test function for CIMClass.__str__() / str()
    """

    # The code to be tested
    s = str(obj)

    assert re.match(r'^CIMClass\(', s)

    exp_classname = _format('classname={0!A}', obj.classname)
    assert exp_classname in s


@pytest.mark.parametrize(
    "obj",
    TESTCASES_CIMCLASS_STR_REPR)
@log_entry_exit
def test_CIMClass_repr(obj):
    """
    Test function for CIMClass.__repr__() / repr()
    """

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

    assert re.match(r'^CIMClass\(', r)

    exp_classname = _format('classname={0!A}', obj.classname)
    assert exp_classname in r

    exp_superclass = _format('superclass={0!A}', obj.superclass)
    assert exp_superclass in r

    exp_properties = _format('properties={0!A}', obj.properties)
    assert exp_properties in r

    exp_methods = _format('methods={0!A}', obj.methods)
    assert exp_methods in r

    exp_qualifiers = _format('qualifiers={0!A}', obj.qualifiers)
    assert exp_qualifiers in r

    exp_path = _format('path={0!A}', obj.path)
    assert exp_path in r


# Some CIMClassName objects for CIMClass.tocimxml() tests

CIMCLASSNAME_CLASSNAME1 = CIMClassName('CIM_Foo')
CIMCLASSNAME_NAMESPACE1 = CIMClassName('CIM_Foo', namespace='interop')
CIMCLASSNAME_HOST1 = CIMClassName('CIM_Foo', namespace='interop',
                                  host='woot.com')

TESTCASES_CIMCLASS_TOCIMXML = [

    # Testcases for CIMClass.tocimxml() and tocimxmlstr()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: CIMClass object to be tested.
    #   * kwargs: Dict of input args for tocimxml() (empty).
    #   * exp_xml_str: Expected CIM-XML string, as a tuple/list of parts.
    # * 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

    # Tests with path and args variations
    (
        "No path",
        dict(
            obj=CIMClass('CIM_Foo'),
            kwargs={},
            exp_xml_str=(
                '<CLASS NAME="CIM_Foo"/>',
            )
        ),
        None, None, True
    ),
    (
        "Path with classname-only",
        dict(
            obj=CIMClass(
                'CIM_Foo',
                path=CIMCLASSNAME_CLASSNAME1,
            ),
            kwargs={},
            exp_xml_str=(
                '<CLASS NAME="CIM_Foo"/>',
            )
        ),
        None, None, True
    ),
    (
        "Path with namespace",
        dict(
            obj=CIMClass(
                'CIM_Foo',
                path=CIMCLASSNAME_NAMESPACE1,
            ),
            kwargs={},
            exp_xml_str=(
                '<CLASS NAME="CIM_Foo"/>',
            )
        ),
        None, None, True
    ),
    (
        "Path with namespace+host",
        dict(
            obj=CIMClass(
                'CIM_Foo',
                path=CIMCLASSNAME_HOST1,
            ),
            kwargs={},
            exp_xml_str=(
                '<CLASS NAME="CIM_Foo"/>',
            )
        ),
        None, None, True
    ),

    # Tests with property variations
    (
        "Two properties",
        dict(
            obj=CIMClass(
                'CIM_Foo',
                properties=[
                    CIMProperty('P1', 'bla'),
                    CIMProperty('P2', 42, type='uint16'),
                ],
            ),
            kwargs={},
            exp_xml_str=(
                '<CLASS NAME="CIM_Foo">',
                '<PROPERTY NAME="P1" TYPE="string">',
                '<VALUE>bla</VALUE>',
                '</PROPERTY>',
                '<PROPERTY NAME="P2" TYPE="uint16">',
                '<VALUE>42</VALUE>',
                '</PROPERTY>',
                '</CLASS>',
            )
        ),
        None, None, True
    ),

    # Tests with method variations
    (
        "Two methods",
        dict(
            obj=CIMClass(
                'CIM_Foo',
                methods=[
                    CIMMethod('M2', return_type='string'),
                    CIMMethod('M1', return_type='uint32'),
                ],
            ),
            kwargs={},
            exp_xml_str=(
                '<CLASS NAME="CIM_Foo">',
                '<METHOD NAME="M2" TYPE="string"/>',
                '<METHOD NAME="M1" TYPE="uint32"/>',
                '</CLASS>',
            )
        ),
        None, None, True
    ),

    # Tests with qualifier variations
    (
        "Two qualifiers",
        dict(
            obj=CIMClass(
                'CIM_Foo',
                qualifiers=[
                    CIMQualifier('Q2', 'bla'),
                    CIMQualifier('Q1', True),
                ],
            ),
            kwargs={},
            exp_xml_str=(
                '<CLASS NAME="CIM_Foo">',
                '<QUALIFIER NAME="Q2" TYPE="string">',
                '<VALUE>bla</VALUE>',
                '</QUALIFIER>',
                '<QUALIFIER NAME="Q1" TYPE="boolean">',
                '<VALUE>TRUE</VALUE>',
                '</QUALIFIER>',
                '</CLASS>',
            )
        ),
        None, None, True
    ),

    # Tests with qualifier, property, method variations
    (
        "Two qualifiers, two properties, two methods",
        dict(
            obj=CIMClass(
                'CIM_Foo',
                qualifiers=[
                    CIMQualifier('Q2', 'bla'),
                    CIMQualifier('Q1', True),
                ],
                properties=[
                    CIMProperty('P2', 42, type='uint16'),
                    CIMProperty('P1', 'bla'),
                ],
                methods=[
                    CIMMethod('M2', return_type='string'),
                    CIMMethod('M1', return_type='uint32'),
                ],
            ),
            kwargs={},
            exp_xml_str=(
                '<CLASS NAME="CIM_Foo">',
                '<QUALIFIER NAME="Q2" TYPE="string">',
                '<VALUE>bla</VALUE>',
                '</QUALIFIER>',
                '<QUALIFIER NAME="Q1" TYPE="boolean">',
                '<VALUE>TRUE</VALUE>',
                '</QUALIFIER>',
                '<PROPERTY NAME="P2" TYPE="uint16">',
                '<VALUE>42</VALUE>',
                '</PROPERTY>',
                '<PROPERTY NAME="P1" TYPE="string">',
                '<VALUE>bla</VALUE>',
                '</PROPERTY>',
                '<METHOD NAME="M2" TYPE="string"/>',
                '<METHOD NAME="M1" TYPE="uint32"/>',
                '</CLASS>',
            )
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMCLASS_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMClass_tocimxml(testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMClass.tocimxml().
    """

    # The code to be tested
    obj_xml = obj.tocimxml(**kwargs)

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

    obj_xml_str = obj_xml.toxml()
    exp_xml_str = ''.join(exp_xml_str)
    validate_cim_xml_obj(obj, obj_xml_str, exp_xml_str)


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMCLASS_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMClass_tocimxmlstr(testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMClass.tocimxmlstr().
    """

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(**kwargs)

    # 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(obj_xml_str, str)

    exp_xml_str = ''.join(exp_xml_str)
    validate_cim_xml_obj(obj, obj_xml_str, exp_xml_str)


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMCLASS_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMClass_tocimxmlstr_indent_int(
        testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMClass.tocimxmlstr() with indent as integer.
    """

    indent = 4
    indent_str = ' ' * indent

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(indent=indent, **kwargs)

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

    obj_xml = obj.tocimxml(**kwargs)  # This is tested elsewhere
    exp_xml_str = obj_xml.toprettyxml(indent=indent_str)

    assert obj_xml_str == exp_xml_str, (
        f"{obj.__class__.__name__}.tocimxmlstr(indent={indent!r}) "
        "returns unexpected CIM-XML string")


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMCLASS_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMClass_tocimxmlstr_indent_str(
        testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMClass.tocimxmlstr() with indent as string.
    """

    indent = 4
    indent_str = ' ' * indent

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(indent=indent_str, **kwargs)

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

    obj_xml = obj.tocimxml(**kwargs)  # This is tested elsewhere
    exp_xml_str = obj_xml.toprettyxml(indent=indent_str)

    assert obj_xml_str == exp_xml_str, (
        f"{obj.__class__.__name__}.tocimxmlstr(indent={indent_str!r}) "
        "returns unexpected CIM-XML string")


TESTCASES_CIMCLASS_TOMOF = [

    # Testcases for CIMClass.tomof()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: CIMClass object to be tested.
    #   * kwargs: Dict of input args to tomof() method
    #   * exp_mof: Expected MOF result string, or None if failed.
    # * 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

    (
        "all components",
        dict(
            obj=CIMClass(
                classname='C1',
                superclass='C2',
                properties=[
                    CIMProperty(
                        'p2', type='string', value="abc",
                        qualifiers=[
                            CIMQualifier('q2', value="qv2", type='string'),
                        ],
                    ),
                ],
                methods=[
                    CIMMethod(
                        'm3', return_type='uint32',
                        qualifiers=[
                            CIMQualifier('q3', value="qv3", type='string'),
                        ],
                        parameters=[
                            CIMParameter(
                                'p4', type='string',
                                qualifiers=[
                                    CIMQualifier('q4', value="qv4",
                                                 type='string'),
                                ],
                            ),
                        ],
                    ),
                ],
                qualifiers=[
                    CIMQualifier('q1', value="qv1", type='string'),
                ],
                path=None
            ),
            kwargs={},
            exp_mof="""\
   [q1 ( "qv1" )]
class C1 : C2 {

      [q2 ( "qv2" )]
   string p2 = "abc";

      [q3 ( "qv3" )]
   uint32 m3(
         [q4 ( "qv4" )]
      string p4);

};
""",
        ),
        None, None, True
    ),
    (
        "class with embedded instance property with a default value",
        dict(
            obj=CIMClass(
                classname='C1',
                properties=[
                    CIMProperty(
                        'p1',
                        value=CIMInstance(
                            classname='CE',
                            properties=[
                                CIMProperty('emb1', value='abc'),
                                CIMProperty('emb2', value=Sint32(-1024)),
                                CIMProperty('emb3', value=True),
                            ],
                        ),
                        type='string',
                        qualifiers=[
                            CIMQualifier('EmbeddedInstance', value="CE",
                                         type='string'),
                        ],
                    ),
                ],
            ),
            kwargs={},
            exp_mof="""\
class C1 {

      [EmbeddedInstance ( "CE" )]
   string p1 =
      "instance of CE {\\n   emb1 = \\"abc\\";\\n   emb2 = -1024;\\n   emb3 = "
      "true;\\n};\\n";

};
""",
        ),
        None, None, True
    ),
    (
        "class with reference property with a multi-line default value",
        dict(
            obj=CIMClass(
                classname='C1',
                properties=[
                    CIMProperty(
                        'p1',
                        value=CIMInstanceName(
                            host='some.long.host.name:5989',
                            namespace='root/cimv2',
                            classname='CIM_ReferencedClass',
                            keybindings=[
                                ('k1', 'key1'),
                                ('k2', 'key2'),
                            ],
                        ),
                        type='reference',
                        reference_class='CIM_ReferencedClass',
                    ),
                ],
            ),
            kwargs={},
            # pylint: disable=line-too-long
            exp_mof="""\
class C1 {

   CIM_ReferencedClass REF p1 =
      "//some.long.host.name:5989/root/cimv2:CIM_ReferencedClass.k1=\\"key1\\",k2"
      "=\\"key2\\"";

};
""",  # noqa: E501
        ),
        None, None, True
    ),
    (
        "Class with name that does not fit onto line by 10",
        dict(
            obj=CIMClass(
                classname='Very_long_class_name',
            ),
            kwargs=dict(
                maxline=18,
            ),
            exp_mof='class Very_long_class_name {\n\n};\n',
        ),
        None, None, True
    ),
    (
        "Class with uint64 property value that does not fit onto line by 1",
        dict(
            obj=CIMClass(
                classname='C1',
                properties=[
                    CIMProperty('p1', value=Uint64(1998012513304050)),
                ],
            ),
            kwargs=dict(
                maxline=22,
            ),
            exp_mof=None,
        ),
        ValueError, None, True
    ),
    (
        "Class with uint64 property value that exactly fits onto line",
        dict(
            obj=CIMClass(
                classname='C1',
                properties=[
                    CIMProperty('p1', value=Uint64(1998012513304050)),
                ],
            ),
            kwargs=dict(
                maxline=23,
            ),
            exp_mof=''
            'class C1 {\n'
            '\n'
            '   uint64 p1 =\n'
            '      1998012513304050;\n'
            '\n'
            '};\n',
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMCLASS_TOMOF)
@simplified_test_function
@log_entry_exit
def test_CIMClass_tomof(testcase, obj, kwargs, exp_mof):
    """
    Test function for CIMClass.tomof().
    """

    # The code to be tested
    mof = obj.tomof(**kwargs)

    # 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(mof, str)
    assert mof == exp_mof


TESTCASES_CIMMETHOD_INIT = [

    # Testcases for CIMMethod.__init__()

    # 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 CIMMethod().
    #   * init_kwargs: Dict of keyword arguments to CIMMethod().
    #   * exp_attrs: Dict of expected attributes of resulting 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

    # Order of positional arguments
    (
        "Verify order of positional arguments",
        dict(
            init_args=[
                'Meth1',
                'string',
                NocaseDict([
                    ('P1', CIMParameter('P1', type='string')),
                ]),
                'CIM_FooParent',
                True,
                NocaseDict([
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                ]),
            ],
            init_kwargs={},
            exp_attrs=dict(
                name='Meth1',
                return_type='string',
                parameters=NocaseDict([
                    ('P1', CIMParameter('P1', type='string')),
                ]),
                class_origin='CIM_FooParent',
                propagated=True,
                qualifiers=NocaseDict([
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                ]),
            )
        ),
        None, None, True
    ),

    # Name tests
    (
        "Verify that bytes name and return type are converted to unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                name=b'FooMethod',
                return_type=b'string'
            ),
            exp_attrs=dict(
                name='FooMethod',
                return_type='string'
            )
        ),
        None, None, True
    ),
    (
        "Verify that unicode name and return type remain unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooMethod',
                return_type='string'
            ),
            exp_attrs=dict(
                name='FooMethod',
                return_type='string'
            )
        ),
        None, None, True
    ),

    # Parameters tests
    (
        "Verify parameters order preservation with list of CIMParameter",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='Meth1', return_type='string',
                parameters=[
                    CIMParameter('P1', type='string'),
                    CIMParameter('P2', type='uint32'),
                ]
            ),
            exp_attrs=dict(
                name='Meth1', return_type='string',
                parameters=NocaseDict([
                    ('P1', CIMParameter('P1', type='string')),
                    ('P2', CIMParameter('P2', type='uint32')),
                ])
            )
        ),
        None, None, True
    ),
    (
        "Verify parameters order preservation with OrderedDict",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='Meth1', return_type='string',
                parameters=OrderedDict([
                    ('P1', CIMParameter('P1', type='string')),
                    ('P2', CIMParameter('P2', type='uint32')),
                ])
            ),
            exp_attrs=dict(
                name='Meth1', return_type='string',
                parameters=NocaseDict([
                    ('P1', CIMParameter('P1', type='string')),
                    ('P2', CIMParameter('P2', type='uint32')),
                ])
            )
        ),
        None, None, True
    ),
    (
        "Verify parameters order preservation with list of tuple(key,val)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='Meth1', return_type='string',
                parameters=[
                    ('P1', CIMParameter('P1', type='string')),
                    ('P2', CIMParameter('P2', type='uint32')),
                ]
            ),
            exp_attrs=dict(
                name='Meth1', return_type='string',
                parameters=NocaseDict([
                    ('P1', CIMParameter('P1', type='string')),
                    ('P2', CIMParameter('P2', type='uint32')),
                ])
            )
        ),
        None, None, True
    ),
    (
        "Verify that parameters dict is converted to NocaseDict",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooMethod',
                return_type='string',
                parameters=dict(P1=CIMPARAMETER_P1_OBJ)
            ),
            exp_attrs=dict(
                name='FooMethod',
                return_type='string',
                parameters=NocaseDict(P1=CIMPARAMETER_P1_OBJ)
            )
        ),
        None, None, True
    ),

    # Qualifiers tests
    (
        "Verify qualifiers order preservation with list of CIMQualifier",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='Meth1', return_type='string',
                qualifiers=[
                    CIMQualifier('Q1', value='Ham'),
                    CIMQualifier('Q2', value='Cheese'),
                ]
            ),
            exp_attrs=dict(
                name='Meth1', return_type='string',
                qualifiers=NocaseDict([
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                    ('Q2', CIMQualifier('Q2', value='Cheese')),
                ])
            )
        ),
        None, None, True
    ),
    (
        "Verify qualifiers order preservation with OrderedDict",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='Meth1', return_type='string',
                qualifiers=OrderedDict([
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                    ('Q2', CIMQualifier('Q2', value='Cheese')),
                ])
            ),
            exp_attrs=dict(
                name='Meth1', return_type='string',
                qualifiers=NocaseDict([
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                    ('Q2', CIMQualifier('Q2', value='Cheese')),
                ])
            )
        ),
        None, None, True
    ),
    (
        "Verify qualifiers order preservation with list of tuple(key,val)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='Meth1', return_type='string',
                qualifiers=[
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                    ('Q2', CIMQualifier('Q2', value='Cheese')),
                ]
            ),
            exp_attrs=dict(
                name='Meth1', return_type='string',
                qualifiers=NocaseDict([
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                    ('Q2', CIMQualifier('Q2', value='Cheese')),
                ])
            )
        ),
        None, None, True
    ),
    (
        "Verify that qualifiers dict is converted to NocaseDict",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooMethod',
                return_type='string',
                qualifiers=dict(Q1=CIMQUALIFIER_Q1_OBJ)
            ),
            exp_attrs=dict(
                name='FooMethod',
                return_type='string',
                qualifiers=NocaseDict(Q1=CIMQUALIFIER_Q1_OBJ)
            )
        ),
        None, None, True
    ),
    (
        "Verify that bytes class_origin is converted to unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooMethod',
                return_type='string',
                class_origin=b'CIM_Origin'
            ),
            exp_attrs=dict(
                name='FooMethod',
                return_type='string',
                class_origin='CIM_Origin'
            )
        ),
        None, None, True
    ),
    (
        "Verify that unicode class_origin remains unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooMethod',
                return_type='string',
                class_origin='CIM_Origin'
            ),
            exp_attrs=dict(
                name='FooMethod',
                return_type='string',
                class_origin='CIM_Origin'
            )
        ),
        None, None, True
    ),
    (
        "Verify that propagated int 42 is converted to bool True",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooMethod',
                return_type='string',
                propagated=42
            ),
            exp_attrs=dict(
                name='FooMethod',
                return_type='string',
                propagated=True
            )
        ),
        None, None, True
    ),
    (
        "Verify that propagated int 0 is converted to bool False",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooMethod',
                return_type='string',
                propagated=0
            ),
            exp_attrs=dict(
                name='FooMethod',
                return_type='string',
                propagated=False
            )
        ),
        None, None, True
    ),
    (
        "Verify that propagated string 'false' is converted to bool True",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooMethod',
                return_type='string',
                propagated='false'
            ),
            exp_attrs=dict(
                name='FooMethod',
                return_type='string',
                propagated=True
            )
        ),
        None, None, True
    ),
    (
        "Verify that propagated string 'true' is converted to bool True",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooMethod',
                return_type='string',
                propagated='true'
            ),
            exp_attrs=dict(
                name='FooMethod',
                return_type='string',
                propagated=True
            )
        ),
        None, None, True
    ),
    (
        "Verify that propagated bool True remains True",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooMethod',
                return_type='string',
                propagated=True
            ),
            exp_attrs=dict(
                name='FooMethod',
                return_type='string',
                propagated=True
            )
        ),
        None, None, True
    ),
    (
        "Verify that propagated bool False remains False",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooMethod',
                return_type='string',
                propagated=False
            ),
            exp_attrs=dict(
                name='FooMethod',
                return_type='string',
                propagated=False
            )
        ),
        None, None, True
    ),

    # Exception testcases
    (
        "Verify that name None fails (with ValueError since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name=None, return_type='string'),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that parameters with item of invalid type fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='M',
                return_type='string',
                parameters=[
                    'xxx_invalid_type'
                ]
            ),
            exp_attrs=None
        ),
        TypeError, None, True
    ),
    (
        "Verify that parameter with inconsistent name fails (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='M',
                return_type='string',
                parameters=dict(
                    P1=CIMParameter('P1_X', type='string')
                )
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that qualifiers with item of invalid type fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='M',
                return_type='string',
                qualifiers=[
                    'xxx_invalid_type'
                ]
            ),
            exp_attrs=None
        ),
        TypeError, None, True
    ),
    (
        "Verify that qualifier with inconsistent name fails (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='M',
                return_type='string',
                qualifiers=dict(
                    Q1=CIMQualifier('Q1_X', value='abc')
                )
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that missing return_type fails (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name='M'),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that return_type None fails (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name='M', return_type=None),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that return_type 'reference' fails (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name='M', return_type='reference'),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that invalid return_type fails (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name='M', return_type='xxx'),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMMETHOD_INIT)
@simplified_test_function
@log_entry_exit
def test_CIMMethod_init(testcase, init_args, init_kwargs, exp_attrs):
    """
    Test function for CIMMethod.__init__()
    """

    # The code to be tested
    obj = CIMMethod(*init_args, **init_kwargs)

    assert not hasattr(obj, '__dict__')

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

    exp_name = exp_attrs['name']
    assert obj.name == exp_name
    assert isinstance(obj.name, type(exp_name))

    exp_return_type = exp_attrs['return_type']
    assert obj.return_type == exp_return_type
    assert isinstance(obj.return_type, type(exp_return_type))

    exp_parameters = exp_attrs.get('parameters', NocaseDict())
    assert obj.parameters == exp_parameters
    assert isinstance(obj.parameters, type(exp_parameters))

    exp_class_origin = exp_attrs.get('class_origin', None)
    assert obj.class_origin == exp_class_origin
    assert isinstance(obj.class_origin, type(exp_class_origin))

    exp_propagated = exp_attrs.get('propagated', None)
    assert obj.propagated == exp_propagated
    assert isinstance(obj.propagated, type(exp_propagated))

    exp_qualifiers = exp_attrs.get('qualifiers', NocaseDict())
    assert obj.qualifiers == exp_qualifiers
    assert isinstance(obj.qualifiers, type(exp_qualifiers))


TESTCASES_CIMMETHOD_COPY = [

    # Testcases for CIMMethod.copy()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj_kwargs: Dict of init kwargs for original CIMMethod.
    # * 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

    (
        "Method returning type string, with qualifiers and parameters",
        dict(
            obj_kwargs=dict(
                name='MethFoo',
                return_type='string',
                class_origin=None,
                propagated=False,
                parameters=[
                    CIMParameter('Param1', type='string'),
                ],
                qualifiers=[
                    CIMQualifier('Qual1', value=True),
                ],
            )
        ),
        None, None, True
    ),
    (
        "Method returning type datetime, no qualifiers or parameters",
        dict(
            obj_kwargs=dict(
                name='MethFoo',
                return_type='datetime',
                class_origin=None,
                propagated=False,
                parameters=None,
                qualifiers=None,
            )
        ),
        None, None, True
    ),
    (
        "Method returning type string, propagated, no qualifiers or parameters",
        dict(
            obj_kwargs=dict(
                name='MethFoo',
                return_type='string',
                class_origin='CIM_FooParent',
                propagated=True,
                parameters=None,
                qualifiers=None,
            )
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMMETHOD_COPY)
@simplified_test_function
@log_entry_exit
def test_CIMMethod_copy(testcase, obj_kwargs):
    """
    Test function for CIMMethod.copy()
    """

    obj1 = CIMMethod(**obj_kwargs)

    # The code to be tested
    obj2 = obj1.copy()

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

    # Verify that the copy is equal to the original (performs deep equality)
    assert obj2 == obj1

    # Verify that the copy is a different object
    assert id(obj2) != id(obj1)

    # Verify that parameters are shallow-copied (see CIMMethod.copy())
    if obj1.parameters is not None:
        assert id(obj2.parameters) != id(obj1.parameters)
        for param_name in obj1.parameters:
            param1 = obj1.parameters[param_name]
            param2 = obj2.parameters[param_name]
            assert id(param1) == id(param2)

    # Verify that qualifiers are shallow-copied (see CIMMethod.copy())
    if obj1.qualifiers is not None:
        assert id(obj2.qualifiers) != id(obj1.qualifiers)
        for qual_name in obj1.qualifiers:
            qual1 = obj1.qualifiers[qual_name]
            qual2 = obj2.qualifiers[qual_name]
            assert id(qual1) == id(qual2)

    # Note: There are no other mutable child objects

    # Verify that the copy can be modified and the original remains unchanged.
    # Most of the attribute setters don't validate the change, because multiple
    # such changes might be needed to make the object consistent again.

    obj1_name = obj1.name
    obj2.name = 'SomeNewMeth'
    assert obj1.name == obj1_name

    obj1_return_type = obj1.return_type
    obj2.return_type = 'uint8' if obj1.return_type == 'string' else 'string'
    assert obj1.return_type == obj1_return_type

    obj1_class_origin = obj1.class_origin
    obj2.class_origin = 'SomeNewClass' if obj1.class_origin is None else None
    assert obj1.class_origin == obj1_class_origin

    obj1_propagated = obj1.propagated
    obj2.propagated = not obj1.propagated
    assert obj1.propagated == obj1_propagated

    obj1_parameters = obj1.parameters
    obj2.parameters = [CIMParameter('SomeNewParam', type='string')]
    assert obj1.parameters == obj1_parameters

    obj1_qualifiers = obj1.qualifiers
    obj2.qualifiers = [CIMQualifier('SomeNewQualifier', value=True)]
    assert obj1.qualifiers == obj1_qualifiers


CIMMETHOD_SETATTR_M1_KWARGS = dict(
    name='M1',
    return_type='string',
    propagated=None,
    class_origin=None,
    parameters=dict(P1=CIMParameter('P1', 'boolean')),
    qualifiers=dict(Q1=CIMQualifier('Q1', False)),
)

TESTCASES_CIMMETHOD_SETATTR = [

    # Testcases for CIMMethod set attribute

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj_kwargs: Dict of init kwargs for CIMMethod.
    #   * item: Name of CIMMethod attr to set, or tuple
    #     (item, key) to set a single item in an attr that is a dict.
    #   * new_value: New value to set.
    #   * exp_attrs: Dict of expected attributes, for validation.
    # * 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

    # Tests that set the name attribute
    (
        "Set name to different string",
        dict(
            obj_kwargs=CIMMETHOD_SETATTR_M1_KWARGS,
            item='name',
            new_value='M2',
            exp_attrs=dict(
                name='M2',
            ),
        ),
        None, None, True
    ),
    (
        "Set name to None",
        # Before 0.12.0, the implementation allowed the name to be None,
        # although the documentation required it not to be None.
        # We test the implemented behavior. Since 0.12.0, this raises
        # ValueError.
        dict(
            obj_kwargs=CIMMETHOD_SETATTR_M1_KWARGS,
            item='name',
            new_value=None,
            exp_attrs=dict(
                name=None,
            ),
        ),
        # raises ValueError since 0.12
        ValueError, None, True
    ),

    # Tests that set the return_type attribute
    (
        "Set return_type from string to uint8",
        dict(
            obj_kwargs=dict(
                name='M1',
                return_type='string',
            ),
            item='return_type',
            new_value='uint8',
            exp_attrs=dict(
                return_type='uint8',
            ),
        ),
        None, None, True
    ),
    (
        "Set return_type to invalid type",
        dict(
            obj_kwargs=dict(
                name='M1',
                return_type='string',
            ),
            item='return_type',
            new_value='xxx',
            exp_attrs=None,
        ),
        ValueError, None, True  # invalid type
    ),
    (
        "Set return_type to None",
        dict(
            obj_kwargs=dict(
                name='M1',
                return_type='string',
            ),
            item='return_type',
            new_value=None,
            exp_attrs=None,
        ),
        ValueError, None, True  # invalid type
    ),

    # Tests that set the propagated attribute
    (
        "For non-propagated method without class origin, set propagated to "
        "True",
        dict(
            obj_kwargs=dict(
                name='M1',
                return_type='string',
                propagated=False,
                class_origin=None,
            ),
            item='propagated',
            new_value=True,
            exp_attrs=dict(
                propagated=True,  # inconsistency with class origin not checked
                class_origin=None,
            ),
        ),
        None, None, True
    ),
    (
        "For non-propagated method with class origin, set propagated to "
        "True",
        dict(
            obj_kwargs=dict(
                name='M1',
                return_type='string',
                propagated=False,
                class_origin='C2',
            ),
            item='propagated',
            new_value=True,
            exp_attrs=dict(
                propagated=True,
                class_origin='C2',
            ),
        ),
        None, None, True
    ),
    (
        "For non-propagated method with class origin, set propagated to "
        "'false' (for boolean conversion to True)",
        dict(
            obj_kwargs=dict(
                name='M1',
                return_type='string',
                propagated='false',
                class_origin='C2',
            ),
            item='propagated',
            new_value=True,
            exp_attrs=dict(
                propagated=True,
                class_origin='C2',
            ),
        ),
        None, None, True
    ),
    (
        "For propagated method with class origin, set propagated to False",
        dict(
            obj_kwargs=dict(
                name='M1',
                return_type='string',
                propagated=True,
                class_origin='C2',
            ),
            item='propagated',
            new_value=False,
            exp_attrs=dict(
                propagated=False,  # incons. with class origin not checked
                class_origin='C2',
            ),
        ),
        None, None, True
    ),

    # Tests that set the class_origin attribute
    (
        "For propagated method without class origin, set class origin",
        dict(
            obj_kwargs=dict(
                name='M1',
                return_type='string',
                propagated=True,
                class_origin=None,
            ),
            item='class_origin',
            new_value='C2',
            exp_attrs=dict(
                propagated=True,
                class_origin='C2',
            ),
        ),
        None, None, True
    ),
    (
        "For propagated method with class origin, set class origin to None",
        dict(
            obj_kwargs=dict(
                name='M1',
                return_type='string',
                propagated=True,
                class_origin='C2',
            ),
            item='class_origin',
            new_value=None,
            exp_attrs=dict(
                propagated=True,
                class_origin=None,  # incons. with propagated not checked
            ),
        ),
        None, None, True
    ),
    (
        "For non-propagated method without class origin, set class origin",
        dict(
            obj_kwargs=dict(
                name='M1',
                return_type='string',
                propagated=False,
                class_origin=None,
            ),
            item='class_origin',
            new_value='C2',
            exp_attrs=dict(
                propagated=False,
                class_origin='C2',  # incons. with propagated not checked
            ),
        ),
        None, None, True
    ),

    # Tests that set the parameters attribute
    (
        "Set parameters to new dict with invalid type instead of CIMParameter",
        dict(
            obj_kwargs=CIMMETHOD_SETATTR_M1_KWARGS,
            item='parameters',
            new_value=dict(P2='xxx_invalid_type'),
            exp_attrs=None,
        ),
        TypeError, None, True
    ),
    (
        "Set parameters to new dict with CIMParameter with correct name",
        dict(
            obj_kwargs=CIMMETHOD_SETATTR_M1_KWARGS,
            item='parameters',
            new_value=dict(P2=CIMParameter('P2', 'boolean')),
            exp_attrs=dict(
                parameters=dict(
                    P2=CIMParameter('P2', 'boolean'),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set parameters to new dict with CIMParameter with incorrect name",
        dict(
            obj_kwargs=CIMMETHOD_SETATTR_M1_KWARGS,
            item='parameters',
            new_value=dict(P2=CIMParameter('P2x', 'boolean')),
            exp_attrs=None,
        ),
        # raises ValueError since 0.12
        ValueError, None, True  # inconsistent name
    ),
    (
        "Set parameters to new dict with CIMParameter with name None",
        dict(
            obj_kwargs=CIMMETHOD_SETATTR_M1_KWARGS,
            item='parameters',
            # Name of CIMParameter object is not None, to get over that check.
            new_value=dict([(None, CIMParameter('Pnone', 'boolean'))]),
            exp_attrs=dict(
                parameters=dict([
                    ('P1', CIMParameter('P1', 'boolean')),
                    (None, CIMParameter('Pnone', 'boolean')),
                ]),
            ),
        ),
        # raises ValueError since 0.12
        ValueError, None, True
    ),
    (
        "Set parameters to new dict with simple value",
        dict(
            obj_kwargs=CIMMETHOD_SETATTR_M1_KWARGS,
            item='parameters',
            new_value=dict(P1=True),
            exp_attrs=dict(
                parameters=dict(
                    P1=CIMParameter('P1', 'boolean')
                ),
            ),
        ),
        TypeError, None, True  # must be CIMParameter
    ),
    (
        "Set parameters to None",
        dict(
            obj_kwargs=CIMMETHOD_SETATTR_M1_KWARGS,
            item='parameters',
            new_value=None,
            exp_attrs=dict(
                parameters={},
            ),
        ),
        None, None, True
    ),
    (
        "Set existing parameter to new CIMParameter with correct name",
        dict(
            obj_kwargs=CIMMETHOD_SETATTR_M1_KWARGS,
            item=('parameters', 'P1'),
            new_value=CIMParameter('P1', 'boolean'),
            exp_attrs=dict(
                parameters=dict(
                    P1=CIMParameter('P1', 'boolean'),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set existing parameter to new CIMParameter with incorrect name",
        dict(
            obj_kwargs=CIMMETHOD_SETATTR_M1_KWARGS,
            item=('parameters', 'P1'),
            new_value=CIMParameter('P1x', 'boolean'),
            # No checking for correct name since this sets an item in a normal
            # NocaseDict:
            exp_attrs=dict(
                parameters=dict(
                    P1=CIMParameter('P1x', 'boolean'),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new parameter to new CIMParameter with correct name",
        dict(
            obj_kwargs=CIMMETHOD_SETATTR_M1_KWARGS,
            item=('parameters', 'P2'),
            new_value=CIMParameter('P2', 'boolean'),
            exp_attrs=dict(
                parameters=dict(
                    P1=CIMParameter('P1', 'boolean'),
                    P2=CIMParameter('P2', 'boolean'),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new parameter to new CIMParameter with incorrect name",
        dict(
            obj_kwargs=CIMMETHOD_SETATTR_M1_KWARGS,
            item=('parameters', 'P2'),
            new_value=CIMParameter('P2x', 'boolean'),
            # No checking for correct name since this sets an item in a normal
            # NocaseDict:
            exp_attrs=dict(
                parameters=dict(
                    P1=CIMParameter('P1', 'boolean'),
                    P2=CIMParameter('P2x', 'boolean'),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new parameter with name None",
        dict(
            obj_kwargs=CIMMETHOD_SETATTR_M1_KWARGS,
            item=('parameters', None),
            # Name of CIMParameter object is not None, to get over that check.
            new_value=CIMParameter('Pnone', 'boolean'),
            exp_attrs=None,
        ),
        ValueError if version_info >= (1, 0) else TypeError, None, True
        # None as key in NocaseDict
    ),

    # Tests that set the qualifiers attribute
    (
        "Set qualifiers to new dict with CIMQualifier with correct name",
        dict(
            obj_kwargs=CIMMETHOD_SETATTR_M1_KWARGS,
            item='qualifiers',
            new_value=dict(Q2=CIMQualifier('Q2', True)),
            exp_attrs=dict(
                qualifiers=dict(
                    Q2=CIMQualifier('Q2', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set qualifiers to new dict with CIMQualifier with incorrect name",
        dict(
            obj_kwargs=CIMMETHOD_SETATTR_M1_KWARGS,
            item='qualifiers',
            new_value=dict(Q2=CIMQualifier('Q2x', True)),
            exp_attrs=None,
        ),
        # raises ValueError since 0.12
        ValueError, None, True  # inconsistent name
    ),
    (
        "Set qualifiers to new dict with CIMQualifier with name None",
        dict(
            obj_kwargs=CIMMETHOD_SETATTR_M1_KWARGS,
            item='qualifiers',
            # Name of CIMQualifier object is not None, to get over that check.
            new_value=dict([(None, CIMQualifier('Qnone', True))]),
            exp_attrs=dict(
                qualifiers=dict([
                    ('Q1', CIMQualifier('Q1', False)),
                    (None, CIMQualifier('Qnone', True)),
                ]),
            ),
        ),
        # raises ValueError since 0.12
        ValueError, None, True
    ),
    (
        "Set qualifiers to new dict with simple value",
        dict(
            obj_kwargs=CIMMETHOD_SETATTR_M1_KWARGS,
            item='qualifiers',
            new_value=dict(Q1=True),
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1', True)
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set qualifiers to None",
        dict(
            obj_kwargs=CIMMETHOD_SETATTR_M1_KWARGS,
            item='qualifiers',
            new_value=None,
            exp_attrs=dict(
                qualifiers={},
            ),
        ),
        None, None, True
    ),
    (
        "Set existing qualifier to new CIMQualifier with correct name",
        dict(
            obj_kwargs=CIMMETHOD_SETATTR_M1_KWARGS,
            item=('qualifiers', 'Q1'),
            new_value=CIMQualifier('Q1', True),
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set existing qualifier to new CIMQualifier with incorrect name",
        dict(
            obj_kwargs=CIMMETHOD_SETATTR_M1_KWARGS,
            item=('qualifiers', 'Q1'),
            new_value=CIMQualifier('Q1x', True),
            # No checking for correct name since this sets an item in a normal
            # NocaseDict:
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1x', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new qualifier to new CIMQualifier with correct name",
        dict(
            obj_kwargs=CIMMETHOD_SETATTR_M1_KWARGS,
            item=('qualifiers', 'Q2'),
            new_value=CIMQualifier('Q2', True),
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1', False),
                    Q2=CIMQualifier('Q2', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new qualifier to new CIMQualifier with incorrect name",
        dict(
            obj_kwargs=CIMMETHOD_SETATTR_M1_KWARGS,
            item=('qualifiers', 'Q2'),
            new_value=CIMQualifier('Q2x', True),
            # No checking for correct name since this sets an item in a normal
            # NocaseDict:
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1', False),
                    Q2=CIMQualifier('Q2x', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new qualifier with name None",
        dict(
            obj_kwargs=CIMMETHOD_SETATTR_M1_KWARGS,
            item=('qualifiers', None),
            # Name of CIMQualifier object is not None, to get over that check.
            new_value=CIMQualifier('Qnone', True),
            exp_attrs=None,
        ),
        ValueError if version_info >= (1, 0) else TypeError, None, True
        # None as key in NocaseDict
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMMETHOD_SETATTR)
@simplified_test_function
@log_entry_exit
def test_CIMMethod_setattr(
        testcase, obj_kwargs, item, new_value, exp_attrs):
    """
    Test function for CIMMethod set attribute
    """

    obj = CIMMethod(**obj_kwargs)

    if isinstance(item, tuple):
        attr_name, attr_key = item
        attr_dict = getattr(obj, attr_name)

        # The code to be tested (for setting a single dict item)
        attr_dict[attr_key] = new_value

    else:
        attr_name = item

        # The code to be tested (for setting the entire attribute)
        setattr(obj, attr_name, new_value)

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

    for attr_name in exp_attrs:
        exp_value = exp_attrs[attr_name]
        value = getattr(obj, attr_name)
        assert value == exp_value


TESTCASES_CIMMETHOD_HASH_EQ = [

    # Testcases for CIMMethod.__hash__() and __eq__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: CIMMethod object #1 to be tested.
    #   * obj2: CIMMethod object #2 to be tested.
    #   * exp_equal: Expected equality of the object hash values.
    # * 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

    # Name tests
    (
        "Name, equal with same lexical case",
        dict(
            obj1=CIMMethod('Meth1', return_type='string'),
            obj2=CIMMethod('Meth1', return_type='string'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Name, equal with different lexical case",
        dict(
            obj1=CIMMethod('Meth1', return_type='string'),
            obj2=CIMMethod('metH1', return_type='string'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Name, different",
        dict(
            obj1=CIMMethod('Meth1', return_type='string'),
            obj2=CIMMethod('Meth1_x', return_type='string'),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Return_type tests
    (
        "Return_type, different",
        dict(
            obj1=CIMMethod('Meth1', return_type='uint8'),
            obj2=CIMMethod('Meth1', return_type='sint8'),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Parameters tests
    (
        "Matching parameters, names with same lexical case",
        dict(
            obj1=CIMMethod('Meth1', return_type='uint8', parameters=[
                CIMParameter('p1', type='string'),
            ]),
            obj2=CIMMethod('Meth1', return_type='uint8', parameters=[
                CIMParameter('p1', type='string'),
            ]),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Matching parameters, names with different lexical case",
        dict(
            obj1=CIMMethod('Meth1', return_type='uint8', parameters=[
                CIMParameter('p1', type='string'),
            ]),
            obj2=CIMMethod('Meth1', return_type='uint8', parameters=[
                CIMParameter('P1', type='string'),
            ]),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Non-matching parameters, one parameter more",
        dict(
            obj1=CIMMethod('Meth1', return_type='uint8', parameters=[
                CIMParameter('p1', type='string'),
            ]),
            obj2=CIMMethod('Meth1', return_type='uint8', parameters=[
                CIMParameter('p1', type='string'),
                CIMParameter('p2', type='string'),
            ]),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching parameters, one parameter less",
        dict(
            obj1=CIMMethod('Meth1', return_type='uint8', parameters=[
                CIMParameter('p1', type='string'),
                CIMParameter('p2', type='string'),
            ]),
            obj2=CIMMethod('Meth1', return_type='uint8', parameters=[
                CIMParameter('p1', type='string'),
            ]),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching parameters, different parameters",
        dict(
            obj1=CIMMethod('Meth1', return_type='uint8', parameters=[
                CIMParameter('p2', type='string'),
            ]),
            obj2=CIMMethod('Meth1', return_type='uint8', parameters=[
                CIMParameter('p1', type='string'),
            ]),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Propagated tests
    (
        "Propagated, equal",
        dict(
            obj1=CIMMethod('Meth1', return_type='string',
                           propagated=True),
            obj2=CIMMethod('Meth1', return_type='string',
                           propagated=True),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Propagated, different",
        dict(
            obj1=CIMMethod('Meth1', return_type='string',
                           propagated=True),
            obj2=CIMMethod('Meth1', return_type='string',
                           propagated=False),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Class_origin tests
    (
        "Class_origin, equal with same lexical case",
        dict(
            obj1=CIMMethod('Meth1', return_type='string',
                           propagated=True, class_origin='CIM_Org'),
            obj2=CIMMethod('Meth1', return_type='string',
                           propagated=True, class_origin='CIM_Org'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Class_origin, equal with different lexical case",
        dict(
            obj1=CIMMethod('Meth1', return_type='string',
                           propagated=True, class_origin='CIM_Org'),
            obj2=CIMMethod('Meth1', return_type='string',
                           propagated=True, class_origin='Cim_org'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Class_origin, different",
        dict(
            obj1=CIMMethod('Meth1', return_type='string',
                           propagated=True, class_origin='CIM_Org'),
            obj2=CIMMethod('Meth1', return_type='string',
                           propagated=True, class_origin='CIM_Org_x'),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Qualifiers tests
    (
        "Matching qualifiers, qualifier names with same lexical case",
        dict(
            obj1=CIMMethod('Meth1', return_type='string',
                           qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMMethod('Meth1', return_type='string',
                           qualifiers={'Cheepy': 'Birds'}),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Matching qualifiers, qualifier names with different lexical case",
        dict(
            obj1=CIMMethod('Meth1', return_type='string',
                           qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMMethod('Meth1', return_type='string',
                           qualifiers={'cheepy': 'Birds'}),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Non-matching qualifiers, one qualifier more",
        dict(
            obj1=CIMMethod('Meth1', return_type='string'),
            obj2=CIMMethod('Meth1', return_type='string',
                           qualifiers={'Cheepy': 'Birds'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching qualifiers, one qualifier less",
        dict(
            obj1=CIMMethod('Meth1', return_type='string',
                           qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMMethod('Meth1', return_type='string'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching qualifiers, different qualifiers",
        dict(
            obj1=CIMMethod('Meth1', return_type='string',
                           qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMMethod('Meth1', return_type='string',
                           qualifiers={'Creepy': 'Ants'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Matching qualifiers, with values that differ in lexical case",
        dict(
            obj1=CIMMethod('Meth1', return_type='string',
                           qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMMethod('Meth1', return_type='string',
                           qualifiers={'Cheepy': 'birds'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Matching qualifiers, with values that are unicode / string",
        dict(
            obj1=CIMMethod('Meth1', return_type='string',
                           qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMMethod('Meth1', return_type='string',
                           qualifiers={'Cheepy': 'Birds'}),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Equal qualifiers with a number of types",
        dict(
            obj1=CIMMethod(
                'Meth1', return_type='string',
                qualifiers={
                    'Name': 'Foo',
                    'Boolean': False,
                    'Number': Uint8(42),
                }
            ),
            obj2=CIMMethod(
                'Meth1', return_type='string',
                qualifiers={
                    'Name': 'Foo',
                    'Boolean': False,
                    'Number': Uint8(42),
                }
            ),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Qualifier with different types: bool True / string 'TRUE'",
        dict(
            obj1=CIMMethod('Meth1', return_type='string',
                           qualifiers={'Foo': True}),
            obj2=CIMMethod('Meth1', return_type='string',
                           qualifiers={'Foo': 'TRUE'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Qualifier with different types: bool False / string 'FALSE'",
        dict(
            obj1=CIMMethod('Meth1', return_type='string',
                           qualifiers={'Foo': False}),
            obj2=CIMMethod('Meth1', return_type='string',
                           qualifiers={'Foo': 'FALSE'}),
            exp_equal=False,
        ),
        None, None, True
    ),
]

TESTCASES_CIMMETHOD_EQ = [

    # Testcases for CIMMethod.__hash__() and __eq__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: CIMMethod object #1 to be tested.
    #   * obj2: CIMMethod object #2 to be tested.
    #   * exp_equal: Expected equality of the object hash values.
    # * 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

    # Exception testcases
    (
        "Name, equal with same lexical case",
        dict(
            obj1=CIMMethod('FooMethod', return_type='string'),
            obj2='FooMethod',
            exp_equal=True,
        ),
        TypeError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMMETHOD_HASH_EQ)
@simplified_test_function
@log_entry_exit
def test_CIMMethod_hash(testcase, obj1, obj2, exp_equal):
    """
    Test function for CIMMethod.__hash__().
    """

    # The code to be tested
    hash1 = hash(obj1)
    hash2 = hash(obj2)

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

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

    if exp_equal:
        assert hash1 == hash2
    else:
        assert hash1 != hash2


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMMETHOD_HASH_EQ + TESTCASES_CIMMETHOD_EQ)
@simplified_test_function
@log_entry_exit
def test_CIMMethod_eq(testcase, obj1, obj2, exp_equal):
    """
    Test function for CIMMethod.__eq__().
    """

    # The code to be tested
    equal = (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

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

    assert equal == exp_equal


TESTCASES_CIMMETHOD_STR_REPR = [

    # Testcases for CIMMethod.__repr__(), __str__() / repr(), str()

    # Each list item is a testcase tuple with these items:
    # * obj: CIMMethod object to be tested.

    (
        CIMMethod(
            name='FooMethod',
            return_type='uint32')
    ),
    (
        CIMMethod(
            name='FooMethod',
            return_type='uint32',
            parameters=dict(P1=CIMParameter('P1', type='uint32')),
            class_origin='CIM_Origin',
            propagated=True,
            qualifiers=dict(Q1=CIMQualifier('Q1', value=True)))
    ),
]


@pytest.mark.parametrize(
    "obj",
    TESTCASES_CIMMETHOD_STR_REPR)
@log_entry_exit
def test_CIMMethod_str(obj):
    """
    Test function for CIMMethod.__str__() / str()
    """

    # The code to be tested
    s = str(obj)

    assert re.match(r'^CIMMethod\(', s)

    exp_name = _format('name={0!A}', obj.name)
    assert exp_name in s

    exp_return_type = _format('return_type={0!A}', obj.return_type)
    assert exp_return_type in s


@pytest.mark.parametrize(
    "obj",
    TESTCASES_CIMMETHOD_STR_REPR)
@log_entry_exit
def test_CIMMethod_repr(obj):
    """
    Test function for CIMMethod.__repr__() / repr()
    """

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

    assert re.match(r'^CIMMethod\(', r)

    exp_name = _format('name={0!A}', obj.name)
    assert exp_name in r

    exp_return_type = _format('return_type={0!A}', obj.return_type)
    assert exp_return_type in r

    exp_parameters = _format('parameters={0!A}', obj.parameters)
    assert exp_parameters in r

    exp_class_origin = _format('class_origin={0!A}', obj.class_origin)
    assert exp_class_origin in r

    exp_propagated = _format('propagated={0!A}', obj.propagated)
    assert exp_propagated in r

    exp_qualifiers = _format('qualifiers={0!A}', obj.qualifiers)
    assert exp_qualifiers in r


TESTCASES_CIMMETHOD_TOCIMXML = [

    # Testcases for CIMMethod.tocimxml() and tocimxmlstr()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: CIMMethod object to be tested.
    #   * kwargs: Dict of input args for tocimxml() (empty).
    #   * exp_xml_str: Expected CIM-XML string, as a tuple/list of parts.
    # * 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

    # Tests with name variations
    (
        "Name with ASCII characters, as byte string",
        dict(
            obj=CIMMethod(b'Foo', return_type='string'),
            kwargs={},
            exp_xml_str=(
                '<METHOD NAME="Foo" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Name with ASCII characters, as unicode string",
        dict(
            obj=CIMMethod('Foo', return_type='string'),
            kwargs={},
            exp_xml_str=(
                '<METHOD NAME="Foo" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Name with non-ASCII UCS-2 characters, as byte string",
        dict(
            obj=CIMMethod(b'Foo\xC3\xA9', return_type='string'),
            kwargs={},
            exp_xml_str=(
                '<METHOD NAME="Foo\u00E9" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Name with non-ASCII UCS-2 characters, as unicode string",
        dict(
            obj=CIMMethod('Foo\u00E9', return_type='string'),
            kwargs={},
            exp_xml_str=(
                '<METHOD NAME="Foo\u00E9" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Name with non-UCS-2 characters, as byte string",
        dict(
            obj=CIMMethod(b'Foo\xF0\x90\x85\x82', return_type='string'),
            kwargs={},
            exp_xml_str=(
                '<METHOD NAME="Foo\U00010142" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Name with non-UCS-2 characters, as unicode string",
        dict(
            obj=CIMMethod('Foo\U00010142', return_type='string'),
            kwargs={},
            exp_xml_str=(
                '<METHOD NAME="Foo\U00010142" TYPE="string"/>',
            )
        ),
        None, None, True
    ),

    # Tests with qualifier variations
    (
        "Two qualifiers",
        dict(
            obj=CIMMethod(
                'Foo', return_type='string',
                qualifiers=[
                    CIMQualifier('Q2', 'bla'),
                    CIMQualifier('Q1', True),
                ],
            ),
            kwargs={},
            exp_xml_str=(
                '<METHOD NAME="Foo" TYPE="string">',
                '<QUALIFIER NAME="Q2" TYPE="string">',
                '<VALUE>bla</VALUE>',
                '</QUALIFIER>',
                '<QUALIFIER NAME="Q1" TYPE="boolean">',
                '<VALUE>TRUE</VALUE>',
                '</QUALIFIER>',
                '</METHOD>',
            )
        ),
        None, None, True
    ),

    # Tests with return type variations (subset, scalar-only)
    # Methods cannot have reference return type (checked by CIMMethod)
    (
        "Return type boolean",
        dict(
            obj=CIMMethod(
                'Foo', return_type='boolean',
            ),
            kwargs={},
            exp_xml_str=(
                '<METHOD NAME="Foo" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),
    (
        "Return type string",
        dict(
            obj=CIMMethod(
                'Foo', return_type='string',
            ),
            kwargs={},
            exp_xml_str=(
                '<METHOD NAME="Foo" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Return type real32",
        dict(
            obj=CIMMethod(
                'Foo', return_type='real32',
            ),
            kwargs={},
            exp_xml_str=(
                '<METHOD NAME="Foo" TYPE="real32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Return type sint64",
        dict(
            obj=CIMMethod(
                'Foo', return_type='sint64',
            ),
            kwargs={},
            exp_xml_str=(
                '<METHOD NAME="Foo" TYPE="sint64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Return type datetime",
        dict(
            obj=CIMMethod(
                'Foo', return_type='datetime',
            ),
            kwargs={},
            exp_xml_str=(
                '<METHOD NAME="Foo" TYPE="datetime"/>',
            )
        ),
        None, None, True
    ),

    # Tests with parameter variations (subset)
    (
        "Two parameters",
        dict(
            obj=CIMMethod(
                'Foo', return_type='uint32',
                parameters=[
                    CIMParameter('P2', type='boolean'),
                    CIMParameter('P1', type='string', is_array=True),
                ],
            ),
            kwargs={},
            exp_xml_str=(
                '<METHOD NAME="Foo" TYPE="uint32">',
                '<PARAMETER NAME="P2" TYPE="boolean"/>',
                '<PARAMETER.ARRAY NAME="P1" TYPE="string"/>',
                '</METHOD>',
            )
        ),
        None, None, True
    ),

    # Tests with class_origin, propagated variations
    (
        "Class origin set",
        dict(
            obj=CIMMethod(
                'Foo', return_type='uint32', class_origin='CIM_Origin',
            ),
            kwargs={},
            exp_xml_str=(
                '<METHOD CLASSORIGIN="CIM_Origin" NAME="Foo" TYPE="uint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Propagated set to True",
        dict(
            obj=CIMMethod(
                'Foo', return_type='uint32', propagated=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<METHOD NAME="Foo" PROPAGATED="true" TYPE="uint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Propagated set to False",
        dict(
            obj=CIMMethod(
                'Foo', return_type='uint32', propagated=False,
            ),
            kwargs={},
            exp_xml_str=(
                '<METHOD NAME="Foo" PROPAGATED="false" TYPE="uint32"/>',
            )
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMMETHOD_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMMethod_tocimxml(testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMMethod.tocimxml().
    """

    # The code to be tested
    obj_xml = obj.tocimxml(**kwargs)

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

    obj_xml_str = obj_xml.toxml()
    exp_xml_str = ''.join(exp_xml_str)
    validate_cim_xml_obj(obj, obj_xml_str, exp_xml_str)


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMMETHOD_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMMethod_tocimxmlstr(testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMMethod.tocimxmlstr().
    """

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(**kwargs)

    # 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(obj_xml_str, str)

    exp_xml_str = ''.join(exp_xml_str)
    validate_cim_xml_obj(obj, obj_xml_str, exp_xml_str)


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMMETHOD_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMMethod_tocimxmlstr_indent_int(testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMMethod.tocimxmlstr() with indent as integer.
    """

    indent = 4
    indent_str = ' ' * indent

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(indent=indent, **kwargs)

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

    obj_xml = obj.tocimxml(**kwargs)  # This is tested elsewhere
    exp_xml_str = obj_xml.toprettyxml(indent=indent_str)

    assert obj_xml_str == exp_xml_str, (
        f"{obj.__class__.__name__}.tocimxmlstr(indent={indent!r}) "
        "returns unexpected CIM-XML string")


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMMETHOD_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMMethod_tocimxmlstr_indent_str(testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMMethod.tocimxmlstr() with indent as string.
    """

    indent = 4
    indent_str = ' ' * indent

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(indent=indent_str, **kwargs)

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

    obj_xml = obj.tocimxml(**kwargs)  # This is tested elsewhere
    exp_xml_str = obj_xml.toprettyxml(indent=indent_str)

    assert obj_xml_str == exp_xml_str, (
        f"{obj.__class__.__name__}.tocimxmlstr(indent={indent_str!r}) "
        "returns unexpected CIM-XML string")


TESTCASES_CIMMETHOD_TOMOF = [

    # Testcases for CIMMethod.tomof()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: CIMMethod object to be tested.
    #   * kwargs: Dict of input args to tomof() method
    #   * exp_mof: Expected MOF result string, or None if failed.
    # * 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

    (
        "all components",
        dict(
            obj=CIMMethod(
                name='M1',
                return_type='string',
                qualifiers=[
                    CIMQualifier('Q1', value='abc', type='string'),
                    CIMQualifier('Q2', value=Uint32(42), type='uint32'),
                ],
                parameters=[
                    CIMParameter(
                        'P1', type='string',
                        qualifiers=[
                            CIMQualifier('Q3', value="def", type='string'),
                            CIMQualifier('Q4', value=Sint32(-3), type='sint32'),
                        ],
                    ),
                ],
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
               [Q1 ( "abc" ),
                Q2 ( 42 )]
            string M1(
                  [Q3 ( "def" ),
                   Q4 ( -3 )]
               string P1);\n""",
        ),
        None, None, True
    ),
    (
        "no qualifiers, no parameters",
        dict(
            obj=CIMMethod(
                name='M1',
                return_type='string',
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
            string M1();\n""",
        ),
        None, None, True
    ),
    (
        "one scalar single line qualifier, no parameters",
        dict(
            obj=CIMMethod(
                name='M1',
                return_type='string',
                qualifiers=[
                    CIMQualifier('Q1', value='abc', type='string'),
                ],
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
               [Q1 ( "abc" )]
            string M1();\n""",
        ),
        None, None, True
    ),
    (
        "one scalar multi line qualifier, no parameters",
        dict(
            obj=CIMMethod(
                name='M1',
                return_type='string',
                qualifiers=[
                    CIMQualifier('Q1', value=('abc def ' * 10 + 'z'),
                                 type='string'),
                ],
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
               [Q1 (
                   "abc def abc def abc def abc def abc def abc def abc def "
                   "abc def abc def abc def z" )]
            string M1();\n""",
        ),
        None, None, True
    ),
    (
        "two scalar single line qualifiers, no parameters",
        dict(
            obj=CIMMethod(
                name='M1',
                return_type='string',
                qualifiers=[
                    CIMQualifier('Q1', value='abc', type='string'),
                    CIMQualifier('Q2', value=Uint32(42), type='uint32'),
                ],
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
               [Q1 ( "abc" ),
                Q2 ( 42 )]
            string M1();\n""",
        ),
        None, None, True
    ),
    (
        "two scalar multi line qualifiers, no parameters",
        dict(
            obj=CIMMethod(
                name='M1',
                return_type='string',
                qualifiers=[
                    CIMQualifier('Q1', value=('abc def ' * 10 + 'z'),
                                 type='string'),
                    CIMQualifier('Q2', value=('rst uvw ' * 10 + 'z'),
                                 type='string'),
                ],
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
               [Q1 (
                   "abc def abc def abc def abc def abc def abc def abc def "
                   "abc def abc def abc def z" ),
                Q2 (
                   "rst uvw rst uvw rst uvw rst uvw rst uvw rst uvw rst uvw "
                   "rst uvw rst uvw rst uvw z" )]
            string M1();\n""",
        ),
        None, None, True
    ),
    (
        "one array single line qualifier, no parameters",
        dict(
            obj=CIMMethod(
                name='M1',
                return_type='string',
                qualifiers=[
                    CIMQualifier('Q1', value=['abc', 'def'], type='string'),
                ],
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
               [Q1 { "abc", "def" }]
            string M1();\n""",
        ),
        None, None, True
    ),
    (
        "one array multi line qualifier with short items, no parameters",
        dict(
            obj=CIMMethod(
                name='M1',
                return_type='string',
                qualifiers=[
                    CIMQualifier(
                        'Q1',
                        value=[f'abcdef{_i:02d}' for _i in range(0, 10)],
                        type='string'),
                ],
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
               [Q1 { "abcdef00", "abcdef01", "abcdef02", "abcdef03",
                   "abcdef04", "abcdef05", "abcdef06", "abcdef07",
                   "abcdef08", "abcdef09" }]
            string M1();\n""",
        ),
        None, None, True
    ),
    (
        "one array multi line qualifier with long items, no parameters",
        dict(
            obj=CIMMethod(
                name='M1',
                return_type='string',
                qualifiers=[
                    CIMQualifier(
                        'Q1',
                        value=['abc def ' * 10 + f'z{_i:02d}'
                               for _i in range(0, 2)],
                        type='string'),
                ],
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
               [Q1 {
                   "abc def abc def abc def abc def abc def abc def abc def "
                   "abc def abc def abc def z00",
                   "abc def abc def abc def abc def abc def abc def abc def "
                   "abc def abc def abc def z01" }]
            string M1();\n""",
        ),
        None, None, True
    ),
    (
        "return type string, two parameters with type string",
        dict(
            obj=CIMMethod(
                name='M1',
                return_type='string',
                parameters=[
                    CIMParameter('P1', type='string'),
                    CIMParameter('P2', type='string'),
                ],
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
            string M1(
               string P1,
               string P2);\n""",
        ),
        # Unpredictable order before 0.12
        None, None, True
    ),
    (
        "return type uint32, one parameter with type sint32",
        dict(
            obj=CIMMethod(
                name='M1',
                return_type='uint32',
                parameters=[
                    CIMParameter('P1', type='sint32'),
                ],
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
            uint32 M1(
               sint32 P1);\n""",
        ),
        None, None, True
    ),
    (
        "Method with name that does not fit onto line by 10",
        dict(
            obj=CIMMethod(
                name='Very_long_method_name',
                return_type='uint32',
            ),
            kwargs=dict(
                indent=3,
                maxline=24,
            ),
            exp_mof='   uint32 Very_long_method_name();\n',
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMMETHOD_TOMOF)
@simplified_test_function
@log_entry_exit
def test_CIMMethod_tomof(testcase, obj, kwargs, exp_mof):
    """
    Test function for CIMMethod.tomof().
    """

    # The code to be tested
    mof = obj.tomof(**kwargs)

    # 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(mof, str)
    assert mof == exp_mof


TESTCASES_CIMPARAMETER_INIT = [

    # Testcases for CIMParameter.__init__()

    # 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 CIMParameter().
    #   * init_kwargs: Dict of keyword arguments to CIMParameter().
    #   * exp_attrs: Dict of expected attributes of resulting 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

    # Order of positional arguments
    (
        "Verify order of positional arguments",
        dict(
            init_args=[
                'Parm1',
                'string',
                None,
                True,
                5,
                NocaseDict([
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                ]),
                [CIMInstance('CIM_Foo')],
                'instance',
            ],
            init_kwargs={},
            exp_attrs=dict(
                name='Parm1',
                type='string',
                reference_class=None,
                is_array=True,
                array_size=5,
                qualifiers=NocaseDict([
                    ('Q1', CIMQualifier('Q1', value='Ham')),
                ]),
                value=[CIMInstance('CIM_Foo')],
                embedded_object='instance',
            )
        ),
        None, None, True
    ),

    # Name tests
    (
        "Verify that bytes name and type are converted to unicode",
        dict(
            init_args=[],
            init_kwargs=dict(name=b'FooParam', type=b'string'),
            exp_attrs=dict(name='FooParam', type='string')
        ),
        None, None, True
    ),
    (
        "Verify that unicode name and type remain unicode",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooParam', type='string'),
            exp_attrs=dict(name='FooParam', type='string')
        ),
        None, None, True
    ),
    (
        "Verify that bytes reference_class is converted to unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='string',
                reference_class=b'CIM_Ref'
            ),
            exp_attrs=dict(
                name='FooParam',
                type='string',
                reference_class='CIM_Ref'
            )
        ),
        None, None, True
    ),
    (
        "Verify that unicode reference_class remains unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='string',
                reference_class='CIM_Ref'
            ),
            exp_attrs=dict(
                name='FooParam',
                type='string',
                reference_class='CIM_Ref'
            )
        ),
        None, None, True
    ),
    (
        "Verify that array reference_class is accepted",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='string',
                is_array=True,
                reference_class=b'CIM_Ref'
            ),
            exp_attrs=dict(
                name='FooParam',
                type='string',
                is_array=True,
                reference_class='CIM_Ref'
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array 42 is converted to bool (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='string',
                is_array=42
            ),
            exp_attrs=dict(
                name='FooParam',
                type='string',
                is_array=True
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array 'false' is converted to bool using Python "
        "bool rules(since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='string',
                is_array='false'
            ),
            exp_attrs=dict(
                name='FooParam',
                type='string',
                is_array=True
            )
        ),
        None, None, True
    ),
    (
        "Verify that unspecified is_array is implied to scalar by value "
        "None (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='string',
                is_array=None,
                value=None
            ),
            exp_attrs=dict(
                name='FooParam',
                type='string',
                is_array=False,
                value=None
            )
        ),
        None, None, True
    ),
    (
        "Verify that unspecified is_array is implied to scalar by scalar "
        "value (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='string',
                is_array=None,
                value='abc'
            ),
            exp_attrs=dict(
                name='FooParam',
                type='string',
                is_array=False,
                value='abc'
            )
        ),
        None, None, True
    ),
    (
        "Verify that unspecified is_array is implied to array by array "
        "value (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='string',
                is_array=None,
                value=['abc']
            ),
            exp_attrs=dict(
                name='FooParam',
                type='string',
                is_array=True,
                value=['abc']
            )
        ),
        None, None, True
    ),
    (
        "Verify that array_size 2 remains int",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='string',
                is_array=True,
                array_size=2
            ),
            exp_attrs=dict(
                name='FooParam',
                type='string',
                is_array=True,
                array_size=2
            )
        ),
        None, None, True
    ),
    (
        "Verify that mismatch between is_array False and array_size is "
        "ignored",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='string',
                value='abc',
                is_array=False,
                array_size=2
            ),
            exp_attrs=dict(
                name='FooParam',
                type='string',
                value='abc',
                is_array=False,
                array_size=2
            )
        ),
        None, None, True
    ),
    (
        "Verify that qualifiers dict is converted to NocaseDict",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='string',
                qualifiers=dict(Q1=CIMQUALIFIER_Q1_OBJ)
            ),
            exp_attrs=dict(
                name='FooParam',
                type='string',
                qualifiers=NocaseDict(Q1=CIMQUALIFIER_Q1_OBJ)
            )
        ),
        None, None, True
    ),

    # Value tests
    (
        "Verify that a string value is converted to unicode",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='string',
                value='abc'
            ),
            exp_attrs=dict(
                name='FooParam',
                type='string',
                value='abc'
            )
        ),
        None, None, True
    ),
    (
        "Verify that an integer value is converted to Uint32",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='uint32',
                value=42
            ),
            exp_attrs=dict(
                name='FooParam',
                type='uint32',
                value=Uint32(42)
            )
        ),
        None, None, True
    ),
    (
        "Verify that an integer value 1 is converted to bool True",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='boolean',
                value=1
            ),
            exp_attrs=dict(
                name='FooParam',
                type='boolean',
                value=True
            )
        ),
        None, None, True
    ),
    (
        "Verify that an integer value 0 is converted to bool False",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='boolean',
                value=0
            ),
            exp_attrs=dict(
                name='FooParam',
                type='boolean',
                value=False
            )
        ),
        None, None, True
    ),
    (
        "Verify that a non-empty string value is converted to bool True",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='boolean',
                value='FALSE'
            ),
            exp_attrs=dict(
                name='FooParam',
                type='boolean',
                value=True
            )
        ),
        None, None, True
    ),
    (
        "Verify that a float value is converted to real32",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='real32',
                value=42.1
            ),
            exp_attrs=dict(
                name='FooParam',
                type='real32',
                value=Real32(42.1)
            )
        ),
        None, None, True
    ),
    (
        "Verify that a datetime string value is converted to CIM datetime",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='datetime',
                value='19980125133015.123456-300'
            ),
            exp_attrs=dict(
                name='FooParam',
                type='datetime',
                value=CIMDateTime('19980125133015.123456-300')
            )
        ),
        None, None, True
    ),
    (
        "Verify that an embedded instance is accepted and embedded_object "
        "defaults to 'instance'",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='string',
                value=CIMInstance('CIM_Emb')
            ),
            exp_attrs=dict(
                name='FooParam',
                type='string',
                value=CIMInstance('CIM_Emb'),
                embedded_object='instance'
            )
        ),
        None, None, True
    ),
    (
        "Verify that an embedded instance is accepted with embedded_object "
        "specified as 'instance'",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='string',
                value=CIMInstance('CIM_Emb'),
                embedded_object='instance'
            ),
            exp_attrs=dict(
                name='FooParam',
                type='string',
                value=CIMInstance('CIM_Emb'),
                embedded_object='instance'
            )
        ),
        None, None, True
    ),
    (
        "Verify that an embedded class is accepted and embedded_object "
        "defaults to 'object'",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='string',
                value=CIMClass('CIM_Emb')
            ),
            exp_attrs=dict(
                name='FooParam',
                type='string',
                value=CIMClass('CIM_Emb'),
                embedded_object='object'
            )
        ),
        None, None, True
    ),
    (
        "Verify that an embedded class is accepted with embedded_object "
        "specified as 'object'",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='string',
                value=CIMClass('CIM_Emb'),
                embedded_object='object'
            ),
            exp_attrs=dict(
                name='FooParam',
                type='string',
                value=CIMClass('CIM_Emb'),
                embedded_object='object'
            )
        ),
        None, None, True
    ),

    # embedded_object=False tests
    (
        "Verify that embedded_object=False results in None",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='string',
                value='abc',
                embedded_object=False
            ),
            exp_attrs=dict(
                name='FooParam',
                type='string',
                value='abc',
                embedded_object=None
            )
        ),
        None, None, True
    ),

    # Value array tests
    (
        "Verify that a string array value causes is_array to be "
        "defaulted to True",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='string',
                value=['abc']
            ),
            exp_attrs=dict(
                name='FooParam',
                type='string',
                is_array=True,
                value=['abc']
            )
        ),
        None, None, True
    ),
    (
        "Verify that an integer array value is converted to [Uint32]",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='uint32',
                value=[42]
            ),
            exp_attrs=dict(
                name='FooParam',
                type='uint32',
                is_array=True,
                value=[Uint32(42)]
            )
        ),
        None, None, True
    ),
    (
        "Verify that an integer array value 1 is converted to bool True",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='boolean',
                value=[1]
            ),
            exp_attrs=dict(
                name='FooParam',
                type='boolean',
                is_array=True,
                value=[True]
            )
        ),
        None, None, True
    ),
    (
        "Verify that an array item None remains None",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='boolean',
                value=[None]
            ),
            exp_attrs=dict(
                name='FooParam',
                type='boolean',
                is_array=True,
                value=[None]
            )
        ),
        None, None, True
    ),
    (
        "Verify that an integer array value 0 is converted to bool False",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='boolean',
                value=[0]
            ),
            exp_attrs=dict(
                name='FooParam',
                type='boolean',
                is_array=True,
                value=[False]
            )
        ),
        None, None, True
    ),
    (
        "Verify that a non-empty string array value is converted to bool "
        "True",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='boolean',
                value=['FALSE']
            ),
            exp_attrs=dict(
                name='FooParam',
                type='boolean',
                is_array=True,
                value=[True]
            )
        ),
        None, None, True
    ),
    (
        "Verify that a float array value is converted to real32",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='real32',
                value=[42.1]
            ),
            exp_attrs=dict(
                name='FooParam',
                type='real32',
                is_array=True,
                value=[Real32(42.1)]
            )
        ),
        None, None, True
    ),
    (
        "Verify that a datetime string array value is converted to CIM "
        "datetime",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='datetime',
                value=['19980125133015.123456-300']
            ),
            exp_attrs=dict(
                name='FooParam',
                type='datetime',
                is_array=True,
                value=[CIMDateTime('19980125133015.123456-300')]
            )
        ),
        None, None, True
    ),
    (
        "Verify that an embedded instance array is accepted and "
        "embedded_object defaults to 'instance'",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='string',
                value=[CIMInstance('CIM_Emb')]
            ),
            exp_attrs=dict(
                name='FooParam',
                type='string',
                is_array=True,
                value=[CIMInstance('CIM_Emb')],
                embedded_object='instance'
            )
        ),
        None, None, True
    ),
    (
        "Verify that an embedded instance array is accepted with "
        "embedded_object specified as 'instance'",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='string',
                value=[CIMInstance('CIM_Emb')],
                embedded_object='instance'
            ),
            exp_attrs=dict(
                name='FooParam',
                type='string',
                is_array=True,
                value=[CIMInstance('CIM_Emb')],
                embedded_object='instance'
            )
        ),
        None, None, True
    ),
    (
        "Verify that an embedded class array is accepted and "
        "embedded_object defaults to 'object'",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='string',
                value=[CIMClass('CIM_Emb')]
            ),
            exp_attrs=dict(
                name='FooParam',
                type='string',
                is_array=True,
                value=[CIMClass('CIM_Emb')],
                embedded_object='object'
            )
        ),
        None, None, True
    ),
    (
        "Verify that an embedded class array is accepted with "
        "embedded_object specified as 'object'",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooParam',
                type='string',
                value=[CIMClass('CIM_Emb')],
                embedded_object='object'
            ),
            exp_attrs=dict(
                name='FooParam',
                type='string',
                is_array=True,
                value=[CIMClass('CIM_Emb')],
                embedded_object='object'
            )
        ),
        None, None, True
    ),

    # Exception testcases
    (
        "Verify that name None fails (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name=None, type='string'),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that type None fails (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name='M', type=None),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that invalid type fails (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name='M', type='xxx'),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that qualifiers with item of invalid type fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='M',
                type='string',
                qualifiers=[
                    'xxx_invalid_type'
                ]
            ),
            exp_attrs=None
        ),
        TypeError, None, True
    ),
    (
        "Verify that qualifier with inconsistent key / name fails "
        "(since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='M',
                type='string',
                qualifiers=dict(Q1=CIMQualifier('Q1_X', 'abc'))
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMPARAMETER_INIT)
@simplified_test_function
@log_entry_exit
def test_CIMParameter_init(testcase, init_args, init_kwargs, exp_attrs):
    """
    Test function for CIMParameter.__init__()
    """

    # The code to be tested
    obj = CIMParameter(*init_args, **init_kwargs)

    assert not hasattr(obj, '__dict__')

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

    exp_name = exp_attrs['name']
    assert obj.name == exp_name
    assert isinstance(obj.name, type(exp_name))

    exp_type = exp_attrs['type']
    assert obj.type == exp_type
    assert isinstance(obj.type, type(exp_type))

    exp_reference_class = exp_attrs.get('reference_class', None)
    assert obj.reference_class == exp_reference_class
    assert isinstance(obj.reference_class, type(exp_reference_class))

    exp_is_array = exp_attrs.get('is_array', False)
    assert obj.is_array == exp_is_array
    assert isinstance(obj.is_array, type(exp_is_array))

    exp_array_size = exp_attrs.get('array_size', None)
    assert obj.array_size == exp_array_size
    assert isinstance(obj.array_size, type(exp_array_size))

    exp_qualifiers = exp_attrs.get('qualifiers', NocaseDict())
    assert obj.qualifiers == exp_qualifiers
    assert isinstance(obj.qualifiers, type(exp_qualifiers))

    exp_value = exp_attrs.get('value', None)
    assert obj.value == exp_value
    assert isinstance(obj.value, type(exp_value))

    exp_embedded_object = exp_attrs.get('embedded_object', None)
    assert obj.embedded_object == exp_embedded_object
    assert isinstance(obj.embedded_object, type(exp_embedded_object))


TESTCASES_CIMPARAMETER_COPY = [

    # Testcases for CIMParameter.copy()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj_kwargs: Dict of init kwargs for original CIMParameter.
    # * 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

    (
        "Scalar parameter of type string, with qualifiers",
        dict(
            obj_kwargs=dict(
                name='ParamFoo',
                type='string',
                is_array=False,
                array_size=None,
                reference_class=None,
                embedded_object=None,
                value='Bar',
                qualifiers=[
                    CIMQualifier('Key', value=True),
                ],
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter of type uint64, no qualifiers",
        dict(
            obj_kwargs=dict(
                name='ParamFoo',
                type='uint64',
                is_array=False,
                array_size=None,
                reference_class=None,
                embedded_object=None,
                value=42,
                qualifiers=None,
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter of type datetime, no qualifiers",
        dict(
            obj_kwargs=dict(
                name='ParamFoo',
                type='datetime',
                is_array=False,
                array_size=None,
                reference_class=None,
                embedded_object=None,
                value='20191005155152.123456-120',
                qualifiers=None,
            )
        ),
        None, None, True
    ),
    (
        "Variable array parameter of type uint8, no qualifiers",
        dict(
            obj_kwargs=dict(
                name='ParamFoo',
                type='uint8',
                is_array=True,
                array_size=None,
                reference_class=None,
                embedded_object=None,
                value=[Uint8(42)],
                qualifiers=None,
            )
        ),
        None, None, True
    ),
    (
        "Fixed array parameter of type uint8, no qualifiers",
        dict(
            obj_kwargs=dict(
                name='ParamFoo',
                type='uint8',
                is_array=True,
                array_size=5,
                reference_class=None,
                embedded_object=None,
                value=[Uint8(42)],
                qualifiers=None,
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter of type reference, no qualifiers",
        dict(
            obj_kwargs=dict(
                name='ParamFoo',
                type='reference',
                is_array=False,
                array_size=None,
                reference_class='CIM_Foo',
                embedded_object=None,
                value=CIMInstanceName('CIM_Foo'),
                qualifiers=None,
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter of type string that is embedded object with "
        "instance value, no qualifiers",
        dict(
            obj_kwargs=dict(
                name='ParamFoo',
                type='string',
                is_array=False,
                array_size=None,
                reference_class=None,
                embedded_object='object',
                value=CIMInstance('CIM_Foo'),
                qualifiers=None,
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter of type string that is embedded object with "
        "class value, no qualifiers",
        dict(
            obj_kwargs=dict(
                name='ParamFoo',
                type='string',
                is_array=False,
                array_size=None,
                reference_class=None,
                embedded_object='object',
                value=CIMClass('CIM_Foo'),
                qualifiers=None,
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter of type string that is embedded instance with "
        "instance value, no qualifiers",
        dict(
            obj_kwargs=dict(
                name='ParamFoo',
                type='string',
                is_array=False,
                array_size=None,
                reference_class=None,
                embedded_object='instance',
                value=CIMInstance('CIM_Foo'),
                qualifiers=None,
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter with propagated and class_origin set, no qualifiers",
        dict(
            obj_kwargs=dict(
                name='ParamFoo',
                type='string',
                is_array=False,
                array_size=None,
                reference_class=None,
                embedded_object=None,
                value='Bar',
                qualifiers=None,
            )
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMPARAMETER_COPY)
@simplified_test_function
@log_entry_exit
def test_CIMParameter_copy(testcase, obj_kwargs):
    """
    Test function for CIMParameter.copy()
    """

    obj1 = CIMParameter(**obj_kwargs)

    # The code to be tested
    obj2 = obj1.copy()

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

    # Verify that the copy is equal to the original (performs deep equality)
    assert obj2 == obj1

    # Verify that the copy is a different object
    assert id(obj2) != id(obj1)

    # TODO: Decide whether mutable child objects should be copied, and add test:
    # # Verify that the mutable child objects are different objects
    # if not isinstance(obj1.value, (int, str)):
    #     assert id(obj2.value) != id(obj1.value)

    # Verify that qualifiers are shallow-copied (see CIMParameter.copy())
    if obj1.qualifiers is not None:
        assert id(obj2.qualifiers) != id(obj1.qualifiers)
        for qual_name in obj1.qualifiers:
            qual1 = obj1.qualifiers[qual_name]
            qual2 = obj2.qualifiers[qual_name]
            assert id(qual1) == id(qual2)

    # Verify that the copy can be modified and the original remains unchanged.
    # Most of the attribute setters don't validate the change, because multiple
    # such changes might be needed to make the object consistent again.

    obj1_name = obj1.name
    obj2.name = 'SomeNewParam'
    assert obj1.name == obj1_name

    obj1_type = obj1.type
    obj2.type = 'uint8' if obj1.type == 'string' else 'string'
    assert obj1.type == obj1_type

    obj1_is_array = obj1.is_array
    obj2.is_array = not obj1.is_array
    assert obj1.is_array == obj1_is_array

    if obj1.is_array:
        obj1_array_size = obj1.array_size
        obj2.array_size = 10 if obj1.array_size is None else None
        assert obj1.array_size == obj1_array_size

    if obj1.type == 'reference':
        obj1_reference_class = obj1.reference_class
        obj2.reference_class = 'SomeNewClass'
        assert obj1.reference_class == obj1_reference_class

    if obj1.embedded_object is not None:
        obj1_embedded_object = obj1.embedded_object
        obj2.reference_class = 'object' \
            if obj1.embedded_object == 'instance' else 'instance'
        assert obj1.embedded_object == obj1_embedded_object

    # TODO: Decide whether value objects should be copied, and add test:
    # obj1_value = obj1.value
    # obj2.value = None  # avoid going through the types
    # assert obj1.value == obj1_value

    obj1_qualifiers = obj1.qualifiers
    obj2.qualifiers = [CIMQualifier('SomeNewQualifier', value=True)]
    assert obj1.qualifiers == obj1_qualifiers


CIMPARAMETER_SETATTR_P1_KWARGS = dict(
    name='P1',
    value='V1',
    type='string',
    reference_class=None,
    embedded_object=None,
    is_array=False,
    array_size=None,
    qualifiers=dict(Q1=CIMQualifier('Q1', False)),
)

TESTCASES_CIMPARAMETER_SETATTR = [

    # Testcases for CIMParameter set attribute

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj_kwargs: Dict of init kwargs for CIMParameter.
    #   * item: Name of CIMParameter attr to set, or tuple
    #     (item, key) to set a single item in an attr that is a dict.
    #   * new_value: New value to set.
    #   * exp_attrs: Dict of expected attributes, for validation.
    # * 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

    # Tests that set the name attribute
    (
        "Set name to different string",
        dict(
            obj_kwargs=CIMPARAMETER_SETATTR_P1_KWARGS,
            item='name',
            new_value='P2',
            exp_attrs=dict(
                name='P2',
            ),
        ),
        None, None, True
    ),
    (
        "Set name to None",
        # Before 0.12.0, the implementation allowed the name to be None,
        # although the documentation required it not to be None.
        # We test the implemented behavior. Since 0.12.0, this raises
        # ValueError.
        dict(
            obj_kwargs=CIMPARAMETER_SETATTR_P1_KWARGS,
            item='name',
            new_value=None,
            exp_attrs=dict(
                name=None,
            ),
        ),
        # raises ValueError since 0.12
        ValueError, None, True
    ),

    # Tests that set the value attribute for scalar string types
    (
        "For scalar string type, set value to 7-bit ASCII unicode string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='value',
            new_value='V2',
            exp_attrs=dict(
                value='V2',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set value to 7-bit ASCII byte string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='value',
            new_value=b'V2',
            exp_attrs=dict(
                value='V2',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set value to non-UCS-2 unicode string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='value',
            new_value='Foo\U00010142',
            exp_attrs=dict(
                value='Foo\U00010142',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set value to non-UCS-2 UTF-8 byte string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='value',
            new_value=b'Foo\xF0\x90\x85\x82',
            exp_attrs=dict(
                value='Foo\U00010142',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for scalar char16 types
    (
        "For scalar char16 type, set value to 7-bit ASCII Char16 string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value=Char16('U'),
            exp_attrs=dict(
                value='U',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar char16 type, set value to 7-bit ASCII unicode string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value='U',
            exp_attrs=dict(
                value='U',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar char16 type, set value to 7-bit ASCII byte string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value=b'U',
            exp_attrs=dict(
                value='U',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar char16 type, set value to non-UCS-2 Char16 string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value=Char16('\U00010142'),
            exp_attrs=dict(
                value='\U00010142',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar char16 type, set value to non-UCS-2 unicode string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value='\U00010142',
            exp_attrs=dict(
                value='\U00010142',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar char16 type, set value to non-UCS-2 UTF-8 byte string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value=b'\xF0\x90\x85\x82',
            exp_attrs=dict(
                value='\U00010142',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar char16 type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for scalar boolean type
    (
        "For scalar boolean type, set value to boolean True",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=False,
                type='boolean',
            ),
            item='value',
            new_value=True,
            exp_attrs=dict(
                value=True,
            ),
        ),
        None, None, True
    ),
    (
        "For scalar boolean type, set value to boolean False",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=True,
                type='boolean',
            ),
            item='value',
            new_value=False,
            exp_attrs=dict(
                value=False,
            ),
        ),
        None, None, True
    ),
    (
        "For scalar boolean type, set value to string 'true'",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=False,
                type='boolean',
            ),
            item='value',
            new_value='true',
            exp_attrs=dict(
                value=True,
            ),
        ),
        None, None, True
    ),
    (
        "For scalar boolean type, set value to string 'false'",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=False,
                type='boolean',
            ),
            item='value',
            new_value='false',
            exp_attrs=dict(
                value=True,  # no processing of 'true'/'false' strings
            ),
        ),
        None, None, True
    ),
    (
        "For scalar boolean type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=False,
                type='boolean',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for scalar integer types
    (
        "For scalar uint8 type, set value to integer out of range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=42,
                type='uint8',
            ),
            item='value',
            new_value=256,
            exp_attrs=dict(
                value=None,
            ),
        ),
        ValueError, None, True  # out of range
    ),
    (
        "For scalar uint64 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=42,
                type='uint64',
            ),
            item='value',
            new_value=12345678901234,
            exp_attrs=dict(
                value=12345678901234,
            ),
        ),
        None, None, True
    ),
    (
        "For scalar sint8 type, set value to integer out of range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=42,
                type='sint8',
            ),
            item='value',
            new_value=-129,
            exp_attrs=dict(
                value=None,
            ),
        ),
        ValueError, None, True  # out of range
    ),
    (
        "For scalar sint64 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=42,
                type='sint64',
            ),
            item='value',
            new_value=-12345678901234,
            exp_attrs=dict(
                value=-12345678901234,
            ),
        ),
        None, None, True
    ),
    (
        "For scalar sint32 type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=42,
                type='sint32',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for scalar real types
    (
        "For scalar real32 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=42.1,
                type='real32',
            ),
            item='value',
            new_value=-12345678890.1,
            exp_attrs=dict(
                value=-12345678890.1,
            ),
        ),
        None, None, True
    ),
    (
        "For scalar real64 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=42.1,
                type='real64',
            ),
            item='value',
            new_value=-12345678890.1,
            exp_attrs=dict(
                value=-12345678890.1,
            ),
        ),
        None, None, True
    ),
    (
        "For scalar real64 type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=42,
                type='real64',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for scalar reference type
    (
        "For scalar reference type, set value to CIMInstanceName",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=CIMINSTANCENAME_C1_OBJ,
                type='reference',
                reference_class='C1',
            ),
            item='value',
            new_value=CIMInstanceName('C2'),
            exp_attrs=dict(
                value=CIMInstanceName('C2'),
                reference_class='C1',  # not set automatically
            ),
        ),
        None, None, True
    ),
    (
        "For scalar reference type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=CIMINSTANCENAME_C1_OBJ,
                type='reference',
                reference_class='C1',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
                reference_class='C1',  # not set automatically
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for scalar datetime type
    (
        "For scalar datetime type, set value to datetime object",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value=datetime(
                year=2019, month=10, day=20, hour=15, minute=30, second=40,
                microsecond=654321, tzinfo=MinutesFromUTC(120)
            ),
            exp_attrs=dict(
                value=CIMDateTime(datetime(
                    year=2019, month=10, day=20, hour=15, minute=30, second=40,
                    microsecond=654321, tzinfo=MinutesFromUTC(120)
                )),
            ),
        ),
        None, None, True
    ),
    (
        "For scalar datetime type, set value to point in time string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value='20191020153040.654321+120',
            exp_attrs=dict(
                value=CIMDateTime('20191020153040.654321+120'),
            ),
        ),
        None, None, True
    ),
    (
        "For scalar datetime type, set value to point in time CIMDateTime",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value=CIMDateTime('20191020153040.654321+120'),
            exp_attrs=dict(
                value=CIMDateTime('20191020153040.654321+120'),
            ),
        ),
        None, None, True
    ),
    (
        "For scalar datetime type, set value to timedelta object",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value=timedelta(25, (14 * 60 + 24) * 60 + 41, 234567),
            exp_attrs=dict(
                value=CIMDateTime(
                    timedelta(25, (14 * 60 + 24) * 60 + 41, 234567)
                ),
            ),
        ),
        None, None, True
    ),
    (
        "For scalar datetime type, set value to interval string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value='00000173232441.234567:000',
            exp_attrs=dict(
                value=CIMDateTime('00000173232441.234567:000'),
            ),
        ),
        None, None, True
    ),
    (
        "For scalar datetime type, set value to interval CIMDateTime",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value=CIMDateTime('00000173232441.234567:000'),
            exp_attrs=dict(
                value=CIMDateTime('00000173232441.234567:000'),
            ),
        ),
        None, None, True
    ),
    (
        "For scalar datetime type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for scalar embedded objects
    (
        "For scalar string type, set value to embedded instance",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='X',
                type='string',
                embedded_object=None,
            ),
            item='value',
            new_value=CIMInstance('C2'),
            exp_attrs=dict(
                value=CIMInstance('C2'),
                embedded_object=None,  # not set automatically
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set value to embedded class",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='X',
                type='string',
                embedded_object=None,
            ),
            item='value',
            new_value=CIMClass('C2'),
            exp_attrs=dict(
                value=CIMClass('C2'),
                embedded_object=None,  # not set automatically
            ),
        ),
        None, None, True
    ),

    # Tests that set array value on scalar parameter and vice versa
    (
        "For array string type, set value to scalar string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value='V2',
            exp_attrs=dict(
                value='V2',  # scalar/array mismatch not checked
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set value to array of strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='value',
            new_value=['V2'],
            exp_attrs=dict(
                value=['V2'],  # scalar/array mismatch not checked
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for array string types
    (
        "For array string type, set value to array of 7-bit ASCII unicode "
        "strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['V2', 'V3'],
            exp_attrs=dict(
                value=['V2', 'V3'],
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set value to array of 7-bit ASCII byte "
        "strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[b'V2', b'V3'],
            exp_attrs=dict(
                value=['V2', 'V3'],
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set value to array of non-UCS-2 unicode "
        "strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['Foo\U00010142'],
            exp_attrs=dict(
                value=['Foo\U00010142'],
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set value to array of non-UCS-2 UTF-8 byte "
        "strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[b'Foo\xF0\x90\x85\x82'],
            exp_attrs=dict(
                value=['Foo\U00010142'],
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set value to empty array",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[],
            exp_attrs=dict(
                value=[],
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set value to array of one None item",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[None],
            exp_attrs=dict(
                value=[None],
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),
    (
        "For fixed array string type, set value to max items",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=2,
            ),
            item='value',
            new_value=['V1', 'V2'],
            exp_attrs=dict(
                value=['V1', 'V2'],
            ),
        ),
        None, None, True
    ),
    (
        "For fixed array string type, set value to one more than max items",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=2,
            ),
            item='value',
            new_value=['V1', 'V2', 'V3'],
            exp_attrs=dict(
                value=['V1', 'V2', 'V3'],  # fixed size not checked
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for array char16 types
    (
        "For array char16 type, set value to array of 7-bit ASCII Char16 "
        "strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[Char16('U'), Char16('V')],
            exp_attrs=dict(
                value=['U', 'V'],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to array of 7-bit ASCII unicode "
        "strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['U', 'V'],
            exp_attrs=dict(
                value=['U', 'V'],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to array of 7-bit ASCII byte "
        "strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[b'U', b'V'],
            exp_attrs=dict(
                value=['U', 'V'],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to array of non-UCS-2 Char16 "
        "strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[Char16('\U00010142')],
            exp_attrs=dict(
                value=['\U00010142'],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to array of non-UCS-2 unicode "
        "strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['\U00010142'],
            exp_attrs=dict(
                value=['\U00010142'],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to array of non-UCS-2 UTF-8 byte "
        "strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[b'\xF0\x90\x85\x82'],
            exp_attrs=dict(
                value=['\U00010142'],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to empty array",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[],
            exp_attrs=dict(
                value=[],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to array of one None item",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[None],
            exp_attrs=dict(
                value=[None],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for array boolean type
    (
        "For array boolean type, set value to array of boolean True",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[False],
                type='boolean',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[True, False],
            exp_attrs=dict(
                value=[True, False],
            ),
        ),
        None, None, True
    ),
    (
        "For array boolean type, set value to array of boolean False",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[True],
                type='boolean',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[False, True],
            exp_attrs=dict(
                value=[False, True],
            ),
        ),
        None, None, True
    ),
    (
        "For array boolean type, set value to array of string 'true'",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[False],
                type='boolean',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['true'],
            exp_attrs=dict(
                value=[True],
            ),
        ),
        None, None, True
    ),
    (
        "For array boolean type, set value to array of string 'false'",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[False],
                type='boolean',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['false'],
            exp_attrs=dict(
                value=[True],  # no processing of 'true'/'false' strings
            ),
        ),
        None, None, True
    ),
    (
        "For array boolean type, set value to empty array",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[False],
                type='boolean',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[],
            exp_attrs=dict(
                value=[],
            ),
        ),
        None, None, True
    ),
    (
        "For array boolean type, set value to array of one None item",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[False],
                type='boolean',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[None],
            exp_attrs=dict(
                value=[None],
            ),
        ),
        None, None, True
    ),
    (
        "For array boolean type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[False],
                type='boolean',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for array integer types
    (
        "For array uint8 type, set value to integer out of range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42],
                type='uint8',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[256],
            exp_attrs=None,
        ),
        ValueError, None, True  # out of range
    ),
    (
        "For array uint64 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42],
                type='uint64',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[12345678901234],
            exp_attrs=dict(
                value=[12345678901234],
            ),
        ),
        None, None, True
    ),
    (
        "For array sint8 type, set value to integer out of range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42],
                type='sint8',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[-129],
            exp_attrs=None,
        ),
        ValueError, None, True  # out of range
    ),
    (
        "For array sint64 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42],
                type='sint64',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[-12345678901234],
            exp_attrs=dict(
                value=[-12345678901234],
            ),
        ),
        None, None, True
    ),
    (
        "For array sint32 type, set value to empty array",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42],
                type='sint32',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[],
            exp_attrs=dict(
                value=[],
            ),
        ),
        None, None, True
    ),
    (
        "For array sint32 type, set value to array of one None item",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42],
                type='sint32',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[None],
            exp_attrs=dict(
                value=[None],
            ),
        ),
        None, None, True
    ),
    (
        "For array sint32 type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42],
                type='sint32',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for array real types
    (
        "For array real32 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42.1],
                type='real32',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[-12345678890.1],
            exp_attrs=dict(
                value=[-12345678890.1],
            ),
        ),
        None, None, True
    ),
    (
        "For array real64 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42.1],
                type='real64',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[-12345678890.1],
            exp_attrs=dict(
                value=[-12345678890.1],
            ),
        ),
        None, None, True
    ),
    (
        "For array real64 type, set value to empty array",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42],
                type='real64',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[],
            exp_attrs=dict(
                value=[],
            ),
        ),
        None, None, True
    ),
    (
        "For array real64 type, set value to array of one None item",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42],
                type='real64',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[None],
            exp_attrs=dict(
                value=[None],
            ),
        ),
        None, None, True
    ),
    (
        "For array real64 type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[42],
                type='real64',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for array reference type
    (
        "For array reference type, set value to array of CIMInstanceName "
        "objects",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[CIMINSTANCENAME_C1_OBJ],
                type='reference',
                reference_class='C1',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[CIMInstanceName('C2')],
            exp_attrs=dict(
                value=[CIMInstanceName('C2')],
                reference_class='C1',  # not set automatically
            ),
        ),
        None, None, True
    ),
    (
        "For array reference type, set value to empty array",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[CIMINSTANCENAME_C1_OBJ],
                type='reference',
                reference_class='C1',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[],
            exp_attrs=dict(
                value=[],
                reference_class='C1',  # not set automatically
            ),
        ),
        None, None, True
    ),
    (
        "For array reference type, set value to array with one None item",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[CIMINSTANCENAME_C1_OBJ],
                type='reference',
                reference_class='C1',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[None],
            exp_attrs=dict(
                value=[None],
                reference_class='C1',  # not set automatically
            ),
        ),
        None, None, True
    ),
    (
        "For array reference type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[CIMINSTANCENAME_C1_OBJ],
                type='reference',
                reference_class='C1',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
                reference_class='C1',  # not set automatically
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for array datetime type
    (
        "For array datetime type, set value to array of datetime objects",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[datetime(
                year=2019, month=10, day=20, hour=15, minute=30, second=40,
                microsecond=654321, tzinfo=MinutesFromUTC(120)
            )],
            exp_attrs=dict(
                value=[CIMDateTime(datetime(
                    year=2019, month=10, day=20, hour=15, minute=30, second=40,
                    microsecond=654321, tzinfo=MinutesFromUTC(120)
                ))],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to array of point in time strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['20191020153040.654321+120'],
            exp_attrs=dict(
                value=[CIMDateTime('20191020153040.654321+120')],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to array of point in time "
        "CIMDateTime objects",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[CIMDateTime('20191020153040.654321+120')],
            exp_attrs=dict(
                value=[CIMDateTime('20191020153040.654321+120')],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to array of timedelta objects",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[timedelta(25, (14 * 60 + 24) * 60 + 41, 234567)],
            exp_attrs=dict(
                value=[CIMDateTime(
                    timedelta(25, (14 * 60 + 24) * 60 + 41, 234567)
                )],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to array of interval strings",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['00000173232441.234567:000'],
            exp_attrs=dict(
                value=[CIMDateTime('00000173232441.234567:000')],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to array of interval CIMDateTime"
        "objects",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[CIMDateTime('00000173232441.234567:000')],
            exp_attrs=dict(
                value=[CIMDateTime('00000173232441.234567:000')],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to empty array",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[],
            exp_attrs=dict(
                value=[],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to array of one None item",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[None],
            exp_attrs=dict(
                value=[None],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for array embedded objects
    (
        "For array string type, set value to embedded instance",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['X'],
                type='string',
                embedded_object=None,
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[CIMInstance('C2')],
            exp_attrs=dict(
                value=[CIMInstance('C2')],
                embedded_object=None,  # not set automatically
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set value to embedded class",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['X'],
                type='string',
                embedded_object=None,
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[CIMClass('C2')],
            exp_attrs=dict(
                value=[CIMClass('C2')],
                embedded_object=None,  # not set automatically
            ),
        ),
        None, None, True
    ),

    # Tests that set the type attribute
    (
        "For scalar string type, set type to uint8",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='type',
            new_value='uint8',
            exp_attrs=dict(
                type='uint8',
                value='V1',
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set type to uint8",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='type',
            new_value='uint8',
            exp_attrs=dict(
                type='uint8',
                value=['V1'],
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set type to invalid type",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='type',
            new_value='xxx',
            exp_attrs=None,
        ),
        ValueError, None, True  # invalid type
    ),
    (
        "For scalar string type, set type to None",
        # Before 0.12.0, the implementation allowed the type to be None,
        # although the documentation required it not to be None.
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='type',
            new_value=None,
            exp_attrs=None,
        ),
        # raises ValueError since 0.12
        ValueError, None, True  # invalid type
    ),

    # Tests that set the reference_class attribute
    (
        "For string type, set reference_class to a class name string",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='reference_class',
            new_value='C2',
            exp_attrs=dict(
                type='string',  # inconsistency with type not checked
                value='V1',
                reference_class='C2',
            ),
        ),
        None, None, True
    ),
    (
        "For reference type, set reference_class to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=CIMInstanceName('C1'),
                type='reference',
                reference_class='C1',
            ),
            item='reference_class',
            new_value=None,
            exp_attrs=dict(
                type='reference',  # inconsistency with type not checked
                value=CIMInstanceName('C1'),
                reference_class=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the embedded_object attribute
    (
        "For string type, set embedded_object to 'instance'",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='embedded_object',
            new_value='instance',
            exp_attrs=dict(
                type='string',
                value='V1',
                embedded_object='instance',  # incons. with value not checked
            ),
        ),
        None, None, True
    ),
    (
        "For string type, set embedded_object to 'object'",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='embedded_object',
            new_value='object',
            exp_attrs=dict(
                type='string',
                value='V1',
                embedded_object='object',  # incons. with value not checked
            ),
        ),
        None, None, True
    ),
    (
        "For string type, set invalid embedded_object",
        # TODO: Clarify whether invalid embedded_object should be checked
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
            ),
            item='embedded_object',
            new_value='xxx',
            exp_attrs=dict(
                type='string',
                value='V1',
                embedded_object='xxx',
            ),
        ),
        None, None, True
    ),
    (
        "For embedded instance, set embedded_object to None",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=CIMInstance('C1'),
                type='string',
                embedded_object='instance',
            ),
            item='embedded_object',
            new_value=None,
            exp_attrs=dict(
                type='string',
                value=CIMInstance('C1'),
                embedded_object=None,  # incons. with value not checked
            ),
        ),
        None, None, True
    ),

    # Tests that set the is_array attribute
    (
        "For scalar string type, set is_array to True",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
                is_array=False,
                array_size=None,
            ),
            item='is_array',
            new_value=True,
            exp_attrs=dict(
                type='string',
                value='V1',
                is_array=True,  # inconsistency with value not checked
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set is_array to 'false' "
        "(for boolean conversion to True)",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
                is_array=False,
                array_size=None,
            ),
            item='is_array',
            new_value='false',
            exp_attrs=dict(
                type='string',
                value='V1',
                is_array=True,  # inconsistency with value not checked
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set is_array to False",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='is_array',
            new_value=False,
            exp_attrs=dict(
                type='string',
                value=['V1'],
                is_array=False,  # inconsistency with value not checked
            ),
        ),
        None, None, True
    ),

    # Tests that set the array_size attribute
    (
        "For scalar string type, set array_size to 2",
        dict(
            obj_kwargs=dict(
                name='P1',
                value='V1',
                type='string',
                is_array=False,
                array_size=None,
            ),
            item='array_size',
            new_value=2,
            exp_attrs=dict(
                type='string',
                value='V1',
                is_array=False,
                array_size=2,  # inconsistency with is_array not checked
            ),
        ),
        None, None, True
    ),
    (
        "For variable array string type, set array_size to 2",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='array_size',
            new_value=2,
            exp_attrs=dict(
                type='string',
                value=['V1'],
                is_array=True,
                array_size=2,
            ),
        ),
        None, None, True
    ),
    (
        "For fixed size 5 array string type with one item, set array_size to "
        "smaller size 2",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=5,
            ),
            item='array_size',
            new_value=2,
            exp_attrs=dict(
                type='string',
                value=['V1'],
                is_array=True,
                array_size=2,
            ),
        ),
        None, None, True
    ),
    (
        "For fixed size 5 array string type with 5 items, set array_size to "
        "too small size 2",
        dict(
            obj_kwargs=dict(
                name='P1',
                value=['V1', 'V2', 'V3', 'V4', 'V5'],
                type='string',
                is_array=True,
                array_size=5,
            ),
            item='array_size',
            new_value=2,
            exp_attrs=dict(
                type='string',
                value=['V1', 'V2', 'V3', 'V4', 'V5'],
                is_array=True,
                array_size=2,  # inconsistency with actual usage not checked
            ),
        ),
        None, None, True
    ),

    # Tests that set the qualifiers attribute
    (
        "Set qualifiers to new dict with CIMQualifier with correct name",
        dict(
            obj_kwargs=CIMPARAMETER_SETATTR_P1_KWARGS,
            item='qualifiers',
            new_value=dict(Q2=CIMQualifier('Q2', True)),
            exp_attrs=dict(
                qualifiers=dict(
                    Q2=CIMQualifier('Q2', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set qualifiers to new dict with CIMQualifier with incorrect name",
        dict(
            obj_kwargs=CIMPARAMETER_SETATTR_P1_KWARGS,
            item='qualifiers',
            new_value=dict(Q2=CIMQualifier('Q2x', True)),
            exp_attrs=None,
        ),
        # raises ValueError since 0.12
        ValueError, None, True  # inconsistent name
    ),
    (
        "Set qualifiers to new dict with CIMQualifier with name None",
        dict(
            obj_kwargs=CIMPARAMETER_SETATTR_P1_KWARGS,
            item='qualifiers',
            # Name of CIMQualifier object is not None, to get over that check.
            new_value=dict([(None, CIMQualifier('Qnone', True))]),
            exp_attrs=dict(
                qualifiers=dict([
                    ('Q1', CIMQualifier('Q1', False)),
                    (None, CIMQualifier('Qnone', True)),
                ]),
            ),
        ),
        # raises ValueError since 0.12
        ValueError, None, True
    ),
    (
        "Set qualifiers to new dict with simple value",
        dict(
            obj_kwargs=CIMPARAMETER_SETATTR_P1_KWARGS,
            item='qualifiers',
            new_value=dict(Q1=True),
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1', True)
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set qualifiers to None",
        dict(
            obj_kwargs=CIMPARAMETER_SETATTR_P1_KWARGS,
            item='qualifiers',
            new_value=None,
            exp_attrs=dict(
                qualifiers={},
            ),
        ),
        None, None, True
    ),
    (
        "Set existing qualifier to new CIMQualifier with correct name",
        dict(
            obj_kwargs=CIMPARAMETER_SETATTR_P1_KWARGS,
            item=('qualifiers', 'Q1'),
            new_value=CIMQualifier('Q1', True),
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set existing qualifier to new CIMQualifier with incorrect name",
        dict(
            obj_kwargs=CIMPARAMETER_SETATTR_P1_KWARGS,
            item=('qualifiers', 'Q1'),
            new_value=CIMQualifier('Q1x', True),
            # No checking for correct name since this sets an item in a normal
            # NocaseDict:
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1x', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new qualifier to new CIMQualifier with correct name",
        dict(
            obj_kwargs=CIMPARAMETER_SETATTR_P1_KWARGS,
            item=('qualifiers', 'Q2'),
            new_value=CIMQualifier('Q2', True),
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1', False),
                    Q2=CIMQualifier('Q2', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new qualifier to new CIMQualifier with incorrect name",
        dict(
            obj_kwargs=CIMPARAMETER_SETATTR_P1_KWARGS,
            item=('qualifiers', 'Q2'),
            new_value=CIMQualifier('Q2x', True),
            # No checking for correct name since this sets an item in a normal
            # NocaseDict:
            exp_attrs=dict(
                qualifiers=dict(
                    Q1=CIMQualifier('Q1', False),
                    Q2=CIMQualifier('Q2x', True),
                ),
            ),
        ),
        None, None, True
    ),
    (
        "Set new qualifier with name None",
        dict(
            obj_kwargs=CIMPARAMETER_SETATTR_P1_KWARGS,
            item=('qualifiers', None),
            # Name of CIMQualifier object is not None, to get over that check.
            new_value=CIMQualifier('Qnone', True),
            exp_attrs=None,
        ),
        ValueError if version_info >= (1, 0) else TypeError, None, True
        # None as key in NocaseDict
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMPARAMETER_SETATTR)
@simplified_test_function
@log_entry_exit
def test_CIMParameter_setattr(
        testcase, obj_kwargs, item, new_value, exp_attrs):
    """
    Test function for CIMParameter set attribute
    """

    obj = CIMParameter(**obj_kwargs)

    if isinstance(item, tuple):
        attr_name, attr_key = item
        attr_dict = getattr(obj, attr_name)

        # The code to be tested (for setting a single dict item)
        attr_dict[attr_key] = new_value

    else:
        attr_name = item

        # The code to be tested (for setting the entire attribute)
        setattr(obj, attr_name, new_value)

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

    for attr_name in exp_attrs:
        exp_value = exp_attrs[attr_name]
        value = getattr(obj, attr_name)
        assert value == exp_value


TESTCASES_CIMPARAMETER_HASH_EQ = [

    # Testcases for CIMParameter.__hash__() and __eq__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: CIMParameter object #1 to be tested.
    #   * obj2: CIMParameter object #2 to be tested.
    #   * exp_equal: Expected equality of the object hash values.
    # * 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

    # Name tests
    (
        "Name, equal with same lexical case",
        dict(
            obj1=CIMParameter('Parm1', type='string'),
            obj2=CIMParameter('Parm1', type='string'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Name, equal with different lexical case",
        dict(
            obj1=CIMParameter('Parm1', type='string'),
            obj2=CIMParameter('pARM1', type='string'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Name, different",
        dict(
            obj1=CIMParameter('Parm1', type='string'),
            obj2=CIMParameter('Parm1_x', type='string'),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Type tests
    (
        "Type, different",
        dict(
            obj1=CIMParameter('Parm1', type='uint8'),
            obj2=CIMParameter('Parm1', type='sint8'),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Reference_class tests
    (
        "Reference class, equal with same lexical case",
        dict(
            obj1=CIMParameter('Parm1', type='reference',
                              reference_class='CIM_Ref'),
            obj2=CIMParameter('Parm1', type='reference',
                              reference_class='CIM_Ref'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Reference class, equal with different lexical case",
        dict(
            obj1=CIMParameter('Parm1', type='reference',
                              reference_class='CIM_Ref'),
            obj2=CIMParameter('Parm1', type='reference',
                              reference_class='Cim_ref'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Reference class, different",
        dict(
            obj1=CIMParameter('Parm1', type='reference',
                              reference_class='CIM_Ref'),
            obj2=CIMParameter('Parm1', type='reference',
                              reference_class='CIM_Ref_x'),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Is_array tests
    (
        "Is_array, equal",
        dict(
            obj1=CIMParameter('Parm1', type='string',
                              is_array=True),
            obj2=CIMParameter('Parm1', type='string',
                              is_array=True),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Is_array, different",
        dict(
            obj1=CIMParameter('Parm1', type='string',
                              is_array=True),
            obj2=CIMParameter('Parm1', type='string',
                              is_array=False),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Array_size tests
    (
        "Array_size, equal",
        dict(
            obj1=CIMParameter('Parm1', type='string', is_array=True,
                              array_size=2),
            obj2=CIMParameter('Parm1', type='string', is_array=True,
                              array_size=2),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Array_size, different",
        dict(
            obj1=CIMParameter('Parm1', type='string', is_array=True,
                              array_size=2),
            obj2=CIMParameter('Parm1', type='string', is_array=True,
                              array_size=3),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Qualifiers tests
    (
        "Matching qualifiers, qualifier names with same lexical case",
        dict(
            obj1=CIMParameter('Parm1', type='string',
                              qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMParameter('Parm1', type='string',
                              qualifiers={'Cheepy': 'Birds'}),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Matching qualifiers, qualifier names with different lexical case",
        dict(
            obj1=CIMParameter('Parm1', type='string',
                              qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMParameter('Parm1', type='string',
                              qualifiers={'cheepy': 'Birds'}),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Non-matching qualifiers, one qualifier more",
        dict(
            obj1=CIMParameter('Parm1', type='string'),
            obj2=CIMParameter('Parm1', type='string',
                              qualifiers={'Cheepy': 'Birds'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching qualifiers, one qualifier less",
        dict(
            obj1=CIMParameter('Parm1', type='string',
                              qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMParameter('Parm1', type='string'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching qualifiers, different qualifiers",
        dict(
            obj1=CIMParameter('Parm1', type='string',
                              qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMParameter('Parm1', type='string',
                              qualifiers={'Creepy': 'Ants'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Matching qualifiers, with values that differ in lexical case",
        dict(
            obj1=CIMParameter('Parm1', type='string',
                              qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMParameter('Parm1', type='string',
                              qualifiers={'Cheepy': 'birds'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Matching qualifiers, with values that are unicode / string",
        dict(
            obj1=CIMParameter('Parm1', type='string',
                              qualifiers={'Cheepy': 'Birds'}),
            obj2=CIMParameter('Parm1', type='string',
                              qualifiers={'Cheepy': 'Birds'}),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Equal qualifiers with a number of types",
        dict(
            obj1=CIMParameter(
                'Parm1', type='string',
                qualifiers={
                    'Name': 'Foo',
                    'Boolean': False,
                    'Number': Uint8(42),
                }
            ),
            obj2=CIMParameter(
                'Parm1', type='string',
                qualifiers={
                    'Name': 'Foo',
                    'Boolean': False,
                    'Number': Uint8(42),
                }
            ),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Qualifier with different types: bool True / string 'TRUE'",
        dict(
            obj1=CIMParameter('Parm1', type='string',
                              qualifiers={'Foo': True}),
            obj2=CIMParameter('Parm1', type='string',
                              qualifiers={'Foo': 'TRUE'}),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Qualifier with different types: bool False / string 'FALSE'",
        dict(
            obj1=CIMParameter('Parm1', type='string',
                              qualifiers={'Foo': False}),
            obj2=CIMParameter('Parm1', type='string',
                              qualifiers={'Foo': 'FALSE'}),
            exp_equal=False,
        ),
        None, None, True
    ),
]

TESTCASES_CIMPARAMETER_EQ = [

    # Additional testcases for CIMParameter.__eq__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: CIMParameter object #1 to be tested.
    #   * obj2: CIMParameter object #2 to be tested.
    #   * exp_equal: Expected equality of the object hash values.
    # * 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

    # Exception testcases
    (
        "Name, equal with same lexical case",
        dict(
            obj1=CIMParameter('Param1', 'uint32'),
            obj2='Param1',
            exp_equal=None,
        ),
        TypeError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMPARAMETER_HASH_EQ)
@simplified_test_function
@log_entry_exit
def test_CIMParameter_hash(testcase, obj1, obj2, exp_equal):
    """
    Test function for CIMParameter.__hash__().
    """

    # The code to be tested
    hash1 = hash(obj1)
    hash2 = hash(obj2)

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

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

    if exp_equal:
        assert hash1 == hash2
    else:
        assert hash1 != hash2


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMPARAMETER_HASH_EQ + TESTCASES_CIMPARAMETER_EQ)
@simplified_test_function
@log_entry_exit
def test_CIMParameter_eq(testcase, obj1, obj2, exp_equal):
    """
    Test function for CIMParameter.__eq__().
    """

    # The code to be tested
    equal = (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

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

    assert equal == exp_equal


TESTCASES_CIMPARAMETER_STR_REPR = [

    # Testcases for CIMParameter.__repr__(), __str__() / repr(), str()

    # Each list item is a testcase tuple with these items:
    # * obj: CIMParameter object to be tested.

    (
        CIMParameter(
            name='Param1',
            type='uint32')
    ),
    (
        CIMParameter(
            name='Param1',
            type='uint32',
            reference_class='CIM_Ref',
            is_array=False,
            array_size=None,
            qualifiers=dict(Q1=CIMQualifier('Q1', value=Uint32(42))),
            value=None)
    ),
]


@pytest.mark.parametrize(
    "obj",
    TESTCASES_CIMPARAMETER_STR_REPR)
@log_entry_exit
def test_CIMParameter_str(obj):
    """
    Test function for CIMParameter.__str__() / str()
    """

    # The code to be tested
    s = str(obj)

    assert re.match(r'^CIMParameter\(', s)

    exp_name = _format('name={0!A}', obj.name)
    assert exp_name in s

    exp_type = _format('type={0!A}', obj.type)
    assert exp_type in s

    exp_reference_class = _format('reference_class={0!A}', obj.reference_class)
    assert exp_reference_class in s

    exp_is_array = _format('is_array={0!A}', obj.is_array)
    assert exp_is_array in s


@pytest.mark.parametrize(
    "obj",
    TESTCASES_CIMPARAMETER_STR_REPR)
@log_entry_exit
def test_CIMParameter_repr(obj):
    """
    Test function for CIMParameter.__repr__() / repr()
    """

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

    assert re.match(r'^CIMParameter\(', r)

    exp_name = _format('name={0!A}', obj.name)
    assert exp_name in r

    exp_type = _format('type={0!A}', obj.type)
    assert exp_type in r

    exp_reference_class = _format('reference_class={0!A}', obj.reference_class)
    assert exp_reference_class in r

    exp_is_array = _format('is_array={0!A}', obj.is_array)
    assert exp_is_array in r

    exp_array_size = _format('array_size={0!A}', obj.array_size)
    assert exp_array_size in r

    exp_qualifiers = _format('qualifiers={0!A}', obj.qualifiers)
    assert exp_qualifiers in r


TESTCASES_CIMPARAMETER_TOCIMXML = [

    # Testcases for CIMParameter.tocimxml() and tocimxmlstr()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: CIMParameter object to be tested.
    #   * kwargs: Dict of input args for tocimxml().
    #   * exp_xml_str: Expected CIM-XML string, as a tuple/list of parts.
    # * 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

    # Parameters with variations of as_value argument
    (
        "Argument as_value defaults to False",
        dict(
            obj=CIMParameter(b'Foo', type='string', value=None),
            kwargs={},
            exp_xml_str=(
                '<PARAMETER NAME="Foo" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Argument as_value specified as False",
        dict(
            obj=CIMParameter(b'Foo', type='string', value=None),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER NAME="Foo" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Argument as_value specified as True",
        dict(
            obj=CIMParameter(b'Foo', type='string', value=None),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="string"/>',
            )
        ),
        None, None, True
    ),

    # Parameters with name variations
    (
        "Parameter as declaration, name with ASCII characters, as byte string",
        dict(
            obj=CIMParameter(b'Foo', type='string'),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER NAME="Foo" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Parameter as declaration, name with ASCII characters, as unicode "
        "string",
        dict(
            obj=CIMParameter('Foo', type='string'),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER NAME="Foo" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Parameter as declaration, name with non-ASCII UCS-2 characters, as "
        "byte string",
        dict(
            obj=CIMParameter(b'Foo\xC3\xA9', value=None, type='string'),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER NAME="Foo\u00E9" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Parameter as declaration, name with non-ASCII UCS-2 characters, as "
        "unicode string",
        dict(
            obj=CIMParameter('Foo\u00E9', value=None, type='string'),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER NAME="Foo\u00E9" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Parameter as declaration, name with non-UCS-2 characters, as byte "
        "string",
        dict(
            obj=CIMParameter(b'Foo\xF0\x90\x85\x82', value=None, type='string'),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER NAME="Foo\U00010142" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Parameter as declaration, name with non-UCS-2 characters, as unicode "
        "string",
        dict(
            obj=CIMParameter('Foo\U00010142', value=None, type='string'),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER NAME="Foo\U00010142" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Parameter as value, name with ASCII characters, as byte string",
        dict(
            obj=CIMParameter(b'Foo', type='string'),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Parameter as value, name with ASCII characters, as unicode string",
        dict(
            obj=CIMParameter('Foo', type='string'),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Parameter as value, name with non-ASCII UCS-2 characters, as byte "
        "string",
        dict(
            obj=CIMParameter(b'Foo\xC3\xA9', value=None, type='string'),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo\u00E9" PARAMTYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Parameter as value, name with non-ASCII UCS-2 characters, as unicode "
        "string",
        dict(
            obj=CIMParameter('Foo\u00E9', value=None, type='string'),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo\u00E9" PARAMTYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Parameter as value, name with non-UCS-2 characters, as byte string",
        dict(
            obj=CIMParameter(b'Foo\xF0\x90\x85\x82', value=None, type='string'),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo\U00010142" PARAMTYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Parameter as value, name with non-UCS-2 characters, as unicode string",
        dict(
            obj=CIMParameter('Foo\U00010142', value=None, type='string'),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo\U00010142" PARAMTYPE="string"/>',
            )
        ),
        None, None, True
    ),

    # Parameters with qualifier variations
    (
        "Parameter as declaration, no qualifiers",
        dict(
            obj=CIMParameter(
                'Foo', type='string', value=None,
                qualifiers=[],
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER NAME="Foo" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Parameter as declaration, two qualifiers and string value",
        dict(
            obj=CIMParameter(
                'Foo', type='string', value='foo',
                qualifiers=[
                    CIMQualifier('Q2', 'bla'),
                    CIMQualifier('Q1', True),
                ],
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER NAME="Foo" TYPE="string">',
                '<QUALIFIER NAME="Q2" TYPE="string">',
                '<VALUE>bla</VALUE>',
                '</QUALIFIER>',
                '<QUALIFIER NAME="Q1" TYPE="boolean">',
                '<VALUE>TRUE</VALUE>',
                '</QUALIFIER>',
                '</PARAMETER>',
            )
        ),
        None, None, True
    ),
    (
        "Parameter as value, no qualifiers",
        dict(
            obj=CIMParameter(
                'Foo', type='string', value=None,
                qualifiers=[],
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Parameter as value, two qualifiers and string value",
        dict(
            obj=CIMParameter(
                'Foo', type='string', value='foo',
                qualifiers=[
                    CIMQualifier('Q2', 'bla'),
                    CIMQualifier('Q1', True),
                ],
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="string">',
                '<VALUE>foo</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Scalar parameters with boolean type
    (
        "Scalar parameter as declaration with boolean type",
        dict(
            obj=CIMParameter(
                'Foo', type='boolean', value=True,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER NAME="Foo" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with boolean type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='boolean', value=None,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="boolean"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with boolean type, value True",
        dict(
            obj=CIMParameter(
                'Foo', type='boolean', value=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="boolean">',
                '<VALUE>TRUE</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with boolean type, value False",
        dict(
            obj=CIMParameter(
                'Foo', type='boolean', value=False,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="boolean">',
                '<VALUE>FALSE</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Scalar parameters with string type
    (
        "Scalar parameter as declaration with string type",
        dict(
            obj=CIMParameter(
                'Foo', type='string', value='bla',
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER NAME="Foo" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with string type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='string', value=None,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with string type, value has one entry with "
        "ASCII characters",
        dict(
            obj=CIMParameter(
                'Foo', type='string', value='foo',
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="string">',
                '<VALUE>foo</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with string type, value has one entry with "
        "non-ASCII UCS-2 characters",
        dict(
            obj=CIMParameter(
                'Foo', type='string', value='foo\u00E9',
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="string">',
                '<VALUE>foo\u00E9</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with string type, value has one entry with "
        "non-UCS-2 characters",
        dict(
            obj=CIMParameter(
                'Foo', type='string', value='foo\U00010142',
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="string">',
                '<VALUE>foo\U00010142</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Scalar parameters with char16 type
    (
        "Scalar parameter as declaration with char16 type",
        dict(
            obj=CIMParameter(
                'Foo', type='char16', value='b',
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER NAME="Foo" TYPE="char16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with char16 type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='char16', value=None,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="char16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with char16 type, value has one entry with "
        "a ASCII character",
        dict(
            obj=CIMParameter(
                'Foo', type='char16', value='f',
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="char16">',
                '<VALUE>f</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with char16 type, value has one entry with "
        "a non-ASCII UCS-2 character",
        dict(
            obj=CIMParameter(
                'Foo', type='char16', value='\u00E9',
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="char16">',
                '<VALUE>\u00E9</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with char16 type, value has one entry with "
        "a non-UCS-2 character "
        "(invalid as per DSP0004, but tolerated by pywbem)",
        dict(
            obj=CIMParameter(
                'Foo', type='char16', value='\U00010142',
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="char16">',
                '<VALUE>\U00010142</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Scalar parameters with uint8 type
    (
        "Scalar parameter as declaration with uint8 type",
        dict(
            obj=CIMParameter(
                'Foo', type='uint8', value=42,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER NAME="Foo" TYPE="uint8"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with uint8 type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='uint8', value=None,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint8"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with uint8 type, value has one entry in "
        "range",
        dict(
            obj=CIMParameter(
                'Foo', type='uint8', value=42,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint8">',
                '<VALUE>42</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Scalar parameters with uint16 type
    (
        "Scalar parameter as declaration with uint16 type",
        dict(
            obj=CIMParameter(
                'Foo', type='uint16', value=42,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER NAME="Foo" TYPE="uint16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with uint16 type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='uint16', value=None,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with uint16 type, value has one entry in "
        "range",
        dict(
            obj=CIMParameter(
                'Foo', type='uint16', value=1234,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint16">',
                '<VALUE>1234</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Scalar parameters with uint32 type
    (
        "Scalar parameter as declaration with uint32 type",
        dict(
            obj=CIMParameter(
                'Foo', type='uint32', value=42,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER NAME="Foo" TYPE="uint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with uint32 type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='uint32', value=None,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with uint32 type, value has one entry in "
        "range",
        dict(
            obj=CIMParameter(
                'Foo', type='uint32', value=12345678,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint32">',
                '<VALUE>12345678</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Scalar parameters with uint64 type
    (
        "Scalar parameter as declaration with uint64 type",
        dict(
            obj=CIMParameter(
                'Foo', type='uint64', value=42,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER NAME="Foo" TYPE="uint64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with uint64 type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='uint64', value=None,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with uint64 type, value has one entry in "
        "range",
        dict(
            obj=CIMParameter(
                'Foo', type='uint64', value=123456789012,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint64">',
                '<VALUE>123456789012</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Scalar parameters with sint8 type
    (
        "Scalar parameter as declaration with sint8 type",
        dict(
            obj=CIMParameter(
                'Foo', type='sint8', value=42,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER NAME="Foo" TYPE="sint8"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with sint8 type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='sint8', value=None,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint8"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with sint8 type, value has one entry in "
        "range",
        dict(
            obj=CIMParameter(
                'Foo', type='sint8', value=-42,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint8">',
                '<VALUE>-42</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Scalar parameters with sint16 type
    (
        "Scalar parameter as declaration with sint16 type",
        dict(
            obj=CIMParameter(
                'Foo', type='sint16', value=42,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER NAME="Foo" TYPE="sint16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with sint16 type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='sint16', value=None,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with sint16 type, value has one entry in "
        "range",
        dict(
            obj=CIMParameter(
                'Foo', type='sint16', value=-1234,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint16">',
                '<VALUE>-1234</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Scalar parameters with sint32 type
    (
        "Scalar parameter as declaration with sint32 type",
        dict(
            obj=CIMParameter(
                'Foo', type='sint32', value=42,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER NAME="Foo" TYPE="sint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with sint32 type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='sint32', value=None,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with sint32 type, value has one entry in "
        "range",
        dict(
            obj=CIMParameter(
                'Foo', type='sint32', value=-12345678,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint32">',
                '<VALUE>-12345678</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Scalar parameters with sint64 type
    (
        "Scalar parameter as declaration with sint64 type",
        dict(
            obj=CIMParameter(
                'Foo', type='sint64', value=42,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER NAME="Foo" TYPE="sint64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with sint64 type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='sint64', value=None,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with sint64 type, value has one entry in "
        "range",
        dict(
            obj=CIMParameter(
                'Foo', type='sint64', value=-123456789012,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint64">',
                '<VALUE>-123456789012</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Scalar parameters with real32 type
    (
        "Scalar parameter as declaration with real32 type",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=42,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER NAME="Foo" TYPE="real32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with real32 type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=None,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with real32 type, value between 0 and 1",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=0.42,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32">',
                '<VALUE>0.42</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with real32 type, value with max number of "
        "significant digits (11)",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=1.2345678901,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32">',
                '<VALUE>1.2345678901</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with real32 type, value larger 1 without "
        "exponent",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=42.0,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32">',
                '<VALUE>42.0</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with real32 type, value with small "
        "negative exponent",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=-42.0E-3,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32">',
                '<VALUE>-0.042</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with real32 type, value with small "
        "positive exponent",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=-42.0E+3,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32">',
                '<VALUE>-42000.0</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with real32 type, value with large "
        "negative exponent",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=-42.0E-30,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32">',
                '<VALUE>-4.2E-29</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with real32 type, value with large "
        "positive exponent",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=-42.0E+30,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32">',
                '<VALUE>-4.2E+31</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with real32 type, special value INF",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=float('inf'),
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32">',
                '<VALUE>INF</VALUE>',  # must be upper case
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with real32 type, special value -INF",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=float('-inf'),
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32">',
                '<VALUE>-INF</VALUE>',  # must be upper case
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with real32 type, special value NaN",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=float('nan'),
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32">',
                '<VALUE>NaN</VALUE>',  # must be upper case
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Scalar parameters with real64 type
    (
        "Scalar parameter as declaration with real64 type",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=42,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER NAME="Foo" TYPE="real64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with real64 type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=None,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with real64 type, value between 0 and 1",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=0.42,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64">',
                '<VALUE>0.42</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, False  # py27: 0.41999999999999998
    ),
    (
        "Scalar parameter as value with real64 type, value with max number of "
        "significant digits (17)",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=1.2345678901234567,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64">',
                '<VALUE>1.2345678901234567</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with real64 type, value larger 1 without "
        "exponent",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=42.0,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64">',
                '<VALUE>42.0</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with real64 type, value with small "
        "negative exponent",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=-42.0E-3,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64">',
                '<VALUE>-0.042</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, False  # py27: -0.042000000000000003
    ),
    (
        "Scalar parameter as value with real64 type, value with small "
        "positive exponent",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=-42.0E+3,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64">',
                '<VALUE>-42000.0</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with real64 type, value with large "
        "negative exponent",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=-42.0E-30,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64">',
                '<VALUE>-4.2E-29</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, False  # py27: -4.1999999999999998E-29
    ),
    (
        "Scalar parameter as value with real64 type, value with large "
        "positive exponent",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=-42.0E+30,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64">',
                '<VALUE>-4.2E+31</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, False  # py27: -4.1999999999999996E+31
    ),
    (
        "Scalar parameter as value with real64 type, special value INF",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=float('inf'),
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64">',
                '<VALUE>INF</VALUE>',  # must be upper case
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with real64 type, special value -INF",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=float('-inf'),
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64">',
                '<VALUE>-INF</VALUE>',  # must be upper case
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with real64 type, special value NaN",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=float('nan'),
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64">',
                '<VALUE>NaN</VALUE>',  # must be upper case
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Scalar parameters with datetime type
    (
        "Scalar parameter as declaration with datetime type",
        dict(
            obj=CIMParameter(
                'Foo', type='datetime',
                value=datetime(2014, 9, 22, 10, 49, 20, 524789),
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER NAME="Foo" TYPE="datetime"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with datetime type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='datetime', value=None,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="datetime"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with datetime type, point in time value",
        dict(
            obj=CIMParameter(
                'Foo', type='datetime',
                value=datetime(2014, 9, 22, 10, 49, 20, 524789),
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="datetime">',
                '<VALUE>20140922104920.524789+000</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with datetime type, interval value",
        dict(
            obj=CIMParameter(
                'Foo', type='datetime',
                value=timedelta(10, 49, 20),
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="datetime">',
                '<VALUE>00000010000049.000020:000</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Scalar parameters with reference type
    (
        "Scalar parameter as declaration with reference type",
        dict(
            obj=CIMParameter(
                'Foo', type='reference',
                value=CIMInstanceName('CIM_Foo'),
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER.REFERENCE NAME="Foo"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with reference type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='reference', value=None,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="reference"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with reference type, classname-only ref "
        "value",
        dict(
            obj=CIMParameter(
                'Foo', type='reference',
                value=CIMInstanceName('CIM_Foo'),
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="reference">',
                '<VALUE.REFERENCE>',
                '<INSTANCENAME CLASSNAME="CIM_Foo"/>',
                '</VALUE.REFERENCE>',
                '</PARAMVALUE>',
            )
        ),
        None, MissingKeybindingsWarning, True
    ),

    # Scalar parameters with embedded object type
    (
        "Scalar parameter as value with embedded instance type",
        dict(
            obj=CIMParameter(
                'Foo', type='string', embedded_object='instance',
                value=CIMInstance('CIM_Foo')
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE EmbeddedObject="instance" NAME="Foo" '
                'PARAMTYPE="string">',
                '<VALUE>&lt;INSTANCE CLASSNAME=&quot;CIM_Foo&quot;/&gt;'
                '</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with embedded object type that is instance",
        dict(
            obj=CIMParameter(
                'Foo', type='string', embedded_object='object',
                value=CIMInstance('CIM_Foo'),
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE EmbeddedObject="object" NAME="Foo" '
                'PARAMTYPE="string">',
                '<VALUE>&lt;INSTANCE CLASSNAME=&quot;CIM_Foo&quot;/&gt;'
                '</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar parameter as value with embedded object type that is class",
        dict(
            obj=CIMParameter(
                'Foo', type='string', embedded_object='object',
                value=CIMClass('CIM_Foo'),
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE EmbeddedObject="object" NAME="Foo" '
                'PARAMTYPE="string">',
                '<VALUE>&lt;CLASS NAME=&quot;CIM_Foo&quot;/&gt;'
                '</VALUE>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Array parameters with boolean type
    (
        "Array parameter as declaration with boolean type",
        dict(
            obj=CIMParameter(
                'Foo', type='boolean', value=[True],
                is_array=True,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER.ARRAY NAME="Foo" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with boolean type, value is NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='boolean', value=None,
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="boolean"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with boolean type, value is empty array",
        dict(
            obj=CIMParameter(
                'Foo', type='boolean', value=[],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="boolean">',
                '<VALUE.ARRAY/>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with boolean type, value has one entry NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='boolean', value=[None],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="boolean">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with boolean type, value has one entry True",
        dict(
            obj=CIMParameter(
                'Foo', type='boolean', value=[True],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="boolean">',
                '<VALUE.ARRAY>',
                '<VALUE>TRUE</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with boolean type, value has one entry False",
        dict(
            obj=CIMParameter(
                'Foo', type='boolean', value=[False],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="boolean">',
                '<VALUE.ARRAY>',
                '<VALUE>FALSE</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Array parameters with string type
    (
        "Array parameter as declaration with string type",
        dict(
            obj=CIMParameter(
                'Foo', type='string', value=['bla'],
                is_array=True,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER.ARRAY NAME="Foo" TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with string type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='string', value=None,
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with string type, value empty array",
        dict(
            obj=CIMParameter(
                'Foo', type='string', value=[],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="string">',
                '<VALUE.ARRAY/>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with string type, value has one entry NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='string', value=[None],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="string">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with string type, value has one entry with "
        "ASCII characters",
        dict(
            obj=CIMParameter(
                'Foo', type='string', value=['foo'],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="string">',
                '<VALUE.ARRAY>',
                '<VALUE>foo</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with string type, value has one entry with "
        "non-ASCII UCS-2 characters",
        dict(
            obj=CIMParameter(
                'Foo', type='string', value=['foo\u00E9'],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="string">',
                '<VALUE.ARRAY>',
                '<VALUE>foo\u00E9</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with string type, value has one entry with "
        "non-UCS-2 characters",
        dict(
            obj=CIMParameter(
                'Foo', type='string', value=['foo\U00010142'],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="string">',
                '<VALUE.ARRAY>',
                '<VALUE>foo\U00010142</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Array parameters with char16 type
    (
        "Array parameter as declaration with char16 type",
        dict(
            obj=CIMParameter(
                'Foo', type='char16', value=['b'],
                is_array=True,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER.ARRAY NAME="Foo" TYPE="char16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with char16 type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='char16', value=None,
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="char16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with char16 type, value empty array",
        dict(
            obj=CIMParameter(
                'Foo', type='char16', value=[],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="char16">',
                '<VALUE.ARRAY/>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with char16 type, value has one entry NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='char16', value=[None],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="char16">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with char16 type, value has one entry with "
        "an ASCII character",
        dict(
            obj=CIMParameter(
                'Foo', type='char16', value=['f'],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="char16">',
                '<VALUE.ARRAY>',
                '<VALUE>f</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with char16 type, value has one entry with "
        "a non-ASCII UCS-2 character",
        dict(
            obj=CIMParameter(
                'Foo', type='char16', value=['\u00E9'],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="char16">',
                '<VALUE.ARRAY>',
                '<VALUE>\u00E9</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with char16 type, value has one entry with "
        "a non-UCS-2 character "
        "(invalid as per DSP0004, but tolerated by pywbem)",
        dict(
            obj=CIMParameter(
                'Foo', type='char16', value=['\U00010142'],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="char16">',
                '<VALUE.ARRAY>',
                '<VALUE>\U00010142</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Array parameters with uint8 type
    (
        "Array parameter as declaration with uint8 type",
        dict(
            obj=CIMParameter(
                'Foo', type='uint8', value=[42],
                is_array=True,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER.ARRAY NAME="Foo" TYPE="uint8"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with uint8 type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='uint8', value=None,
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint8"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with uint8 type, value empty array",
        dict(
            obj=CIMParameter(
                'Foo', type='uint8', value=[],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint8">',
                '<VALUE.ARRAY/>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with uint8 type, value has one entry NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='uint8', value=[None],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint8">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with uint8 type, value has one entry in "
        "range",
        dict(
            obj=CIMParameter(
                'Foo', type='uint8', value=[42],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint8">',
                '<VALUE.ARRAY>',
                '<VALUE>42</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Array parameters with uint16 type
    (
        "Array parameter as declaration with uint16 type",
        dict(
            obj=CIMParameter(
                'Foo', type='uint16', value=[42],
                is_array=True,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER.ARRAY NAME="Foo" TYPE="uint16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with uint16 type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='uint16', value=None,
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with uint16 type, value empty array",
        dict(
            obj=CIMParameter(
                'Foo', type='uint16', value=[],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint16">',
                '<VALUE.ARRAY/>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with uint16 type, value has one entry NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='uint16', value=[None],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint16">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with uint16 type, value has one entry in "
        "range",
        dict(
            obj=CIMParameter(
                'Foo', type='uint16', value=[1234],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint16">',
                '<VALUE.ARRAY>',
                '<VALUE>1234</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Array parameters with uint32 type
    (
        "Array parameter as declaration with uint32 type",
        dict(
            obj=CIMParameter(
                'Foo', type='uint32', value=[42],
                is_array=True,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER.ARRAY NAME="Foo" TYPE="uint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with uint32 type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='uint32', value=None,
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with uint32 type, value empty array",
        dict(
            obj=CIMParameter(
                'Foo', type='uint32', value=[],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint32">',
                '<VALUE.ARRAY/>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with uint32 type, value has one entry NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='uint32', value=[None],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint32">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with uint32 type, value has one entry in "
        "range",
        dict(
            obj=CIMParameter(
                'Foo', type='uint32', value=[12345678],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint32">',
                '<VALUE.ARRAY>',
                '<VALUE>12345678</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Array properties with uint64 type
    (
        "Array parameter as declaration with uint64 type",
        dict(
            obj=CIMParameter(
                'Foo', type='uint64', value=[42],
                is_array=True,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER.ARRAY NAME="Foo" TYPE="uint64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with uint64 type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='uint64', value=None,
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with uint64 type, value empty array",
        dict(
            obj=CIMParameter(
                'Foo', type='uint64', value=[],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint64">',
                '<VALUE.ARRAY/>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with uint64 type, value has one entry NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='uint64', value=[None],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint64">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with uint64 type, value has one entry in "
        "range",
        dict(
            obj=CIMParameter(
                'Foo', type='uint64', value=[123456789012],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="uint64">',
                '<VALUE.ARRAY>',
                '<VALUE>123456789012</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Array parameters with sint8 type
    (
        "Array parameter as declaration with sint8 type",
        dict(
            obj=CIMParameter(
                'Foo', type='sint8', value=[42],
                is_array=True,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER.ARRAY NAME="Foo" TYPE="sint8"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with sint8 type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='sint8', value=None,
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint8"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with sint8 type, value empty array",
        dict(
            obj=CIMParameter(
                'Foo', type='sint8', value=[],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint8">',
                '<VALUE.ARRAY/>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with sint8 type, value has one entry NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='sint8', value=[None],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint8">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with sint8 type, value has one entry in "
        "range",
        dict(
            obj=CIMParameter(
                'Foo', type='sint8', value=[-42],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint8">',
                '<VALUE.ARRAY>',
                '<VALUE>-42</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Array parameters with sint16 type
    (
        "Array parameter as declaration with sint16 type",
        dict(
            obj=CIMParameter(
                'Foo', type='sint16', value=[42],
                is_array=True,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER.ARRAY NAME="Foo" TYPE="sint16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with sint16 type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='sint16', value=None,
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with sint16 type, value empty array",
        dict(
            obj=CIMParameter(
                'Foo', type='sint16', value=[],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint16">',
                '<VALUE.ARRAY/>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with sint16 type, value has one entry NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='sint16', value=[None],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint16">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with sint16 type, value has one entry in "
        "range",
        dict(
            obj=CIMParameter(
                'Foo', type='sint16', value=[-1234],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint16">',
                '<VALUE.ARRAY>',
                '<VALUE>-1234</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Array parameters with sint32 type
    (
        "Array parameter as declaration with sint32 type",
        dict(
            obj=CIMParameter(
                'Foo', type='sint32', value=[42],
                is_array=True,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER.ARRAY NAME="Foo" TYPE="sint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with sint32 type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='sint32', value=None,
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with sint32 type, value empty array",
        dict(
            obj=CIMParameter(
                'Foo', type='sint32', value=[],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint32">',
                '<VALUE.ARRAY/>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with sint32 type, value has one entry NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='sint32', value=[None],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint32">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with sint32 type, value has one entry in "
        "range",
        dict(
            obj=CIMParameter(
                'Foo', type='sint32', value=[-12345678],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint32">',
                '<VALUE.ARRAY>',
                '<VALUE>-12345678</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Array parameters with sint64 type
    (
        "Array parameter as declaration with sint64 type",
        dict(
            obj=CIMParameter(
                'Foo', type='sint64', value=[42],
                is_array=True,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER.ARRAY NAME="Foo" TYPE="sint64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with sint64 type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='sint64', value=None,
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with sint64 type, value empty array",
        dict(
            obj=CIMParameter(
                'Foo', type='sint64', value=[],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint64">',
                '<VALUE.ARRAY/>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with sint64 type, value has one entry NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='sint64', value=[None],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint64">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with sint64 type, value has one entry in "
        "range",
        dict(
            obj=CIMParameter(
                'Foo', type='sint64', value=[-123456789012],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="sint64">',
                '<VALUE.ARRAY>',
                '<VALUE>-123456789012</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Array parameters with real32 type
    (
        "Array parameter as declaration with real32 type",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=[42],
                is_array=True,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER.ARRAY NAME="Foo" TYPE="real32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with real32 type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=None,
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with real32 type, value empty array",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=[],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32">',
                '<VALUE.ARRAY/>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with real32 type, value has one entry NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=[None],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with real32 type, value between 0 and 1",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=[0.42],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>0.42</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with real32 type, value with max number of "
        "significant digits (11)",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=[1.2345678901],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>1.2345678901</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with real32 type, value larger 1 without "
        "exponent",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=[42.0],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>42.0</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with real32 type, value has one entry with "
        "small negative exponent",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=[-42.0E-3],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>-0.042</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with real32 type, value has one entry with "
        "small positive exponent",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=[-42.0E+3],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>-42000.0</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with real32 type, value has one entry with "
        "large negative exponent",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=[-42.0E-30],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>-4.2E-29</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with real32 type, value has one entry with "
        "large positive exponent",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=[-42.0E+30],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>-4.2E+31</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with real32 type, value has one entry with "
        "special value INF",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=[float('inf')],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>INF</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with real32 type, value has one entry with "
        "special value -INF",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=[float('-inf')],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>-INF</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with real32 type, value has one entry with "
        "special value NaN",
        dict(
            obj=CIMParameter(
                'Foo', type='real32', value=[float('nan')],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>NaN</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Array parameters with real64 type
    (
        "Array parameter as declaration with real64 type",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=[42],
                is_array=True,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER.ARRAY NAME="Foo" TYPE="real64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with real64 type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=None,
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with real64 type, value empty array",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=[],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64">',
                '<VALUE.ARRAY/>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with real64 type, value has one entry NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=[None],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with real64 type, value between 0 and 1",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=[0.42],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>0.42</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, False  # py27: 0.41999999999999998
    ),
    (
        "Array parameter as value with real64 type, value has one entry with "
        "max number of significant digits (17)",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=[1.2345678901234567],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>1.2345678901234567</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with real64 type, value has one entry "
        "larger 1 without exponent",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=[42.0],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>42.0</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with real64 type, value has one entry with "
        "small negative exponent",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=[-42.0E-3],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>-0.042</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, False  # py27: -0.042000000000000003
    ),
    (
        "Array parameter as value with real64 type, value has one entry with "
        "small positive exponent",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=[-42.0E+3],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>-42000.0</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with real64 type, value has one entry with "
        "large negative exponent",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=[-42.0E-30],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>-4.2E-29</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, False  # py27: -4.1999999999999998E-29
    ),
    (
        "Array parameter as value with real64 type, value has one entry with "
        "large positive exponent",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=[-42.0E+30],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>-4.2E+31</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, False  # py27: -4.1999999999999996E+31
    ),
    (
        "Array parameter as value with real64 type, special value INF",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=[float('inf')],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>INF</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with real64 type, special value -INF",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=[float('-inf')],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>-INF</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with real64 type, special value NaN",
        dict(
            obj=CIMParameter(
                'Foo', type='real64', value=[float('nan')],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>NaN</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Array parameters with datetime type
    (
        "Array parameter as declaration with datetime type",
        dict(
            obj=CIMParameter(
                'Foo', type='datetime',
                value=[datetime(2014, 9, 22, 10, 49, 20, 524789)],
                is_array=True,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER.ARRAY NAME="Foo" TYPE="datetime"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with datetime type, value NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='datetime', value=None,
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="datetime"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with datetime type, value empty array",
        dict(
            obj=CIMParameter(
                'Foo', type='datetime', value=[],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="datetime">',
                '<VALUE.ARRAY/>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with datetime type, value has one entry NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='datetime', value=[None],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="datetime">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with datetime type, value has one entry "
        "point in time",
        dict(
            obj=CIMParameter(
                'Foo', type='datetime',
                value=[datetime(2014, 9, 22, 10, 49, 20, 524789)],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="datetime">',
                '<VALUE.ARRAY>',
                '<VALUE>20140922104920.524789+000</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with datetime type, value has one entry "
        "interval",
        dict(
            obj=CIMParameter(
                'Foo', type='datetime',
                value=[timedelta(10, 49, 20)],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="datetime">',
                '<VALUE.ARRAY>',
                '<VALUE>00000010000049.000020:000</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),

    # Array parameters with reference type
    (
        "Array parameter as declaration with reference type",
        dict(
            obj=CIMParameter(
                'Foo', type='reference',
                value=[CIMInstanceName('CIM_Foo')],
                is_array=True,
            ),
            kwargs=dict(as_value=False),
            exp_xml_str=(
                '<PARAMETER.REFARRAY NAME="Foo"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with reference type, value has one entry "
        "NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='reference', value=None,
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="reference"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with reference type, value empty array",
        dict(
            obj=CIMParameter(
                'Foo', type='reference', value=[],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="reference">',
                '<VALUE.REFARRAY/>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with reference type, value has one entry "
        "NULL",
        dict(
            obj=CIMParameter(
                'Foo', type='reference', value=[None],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="reference">',
                '<VALUE.REFARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.REFARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with reference type, value has one entry "
        "class-name only ref",
        dict(
            obj=CIMParameter(
                'Foo', type='reference',
                value=[CIMInstanceName('CIM_Foo')],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE NAME="Foo" PARAMTYPE="reference">',
                '<VALUE.REFARRAY>',
                '<VALUE.REFERENCE>',
                '<INSTANCENAME CLASSNAME="CIM_Foo"/>',
                '</VALUE.REFERENCE>',
                '</VALUE.REFARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, MissingKeybindingsWarning, True
    ),

    # Array parameters with embedded object type
    (
        "Array parameter as value with embedded instance type",
        dict(
            obj=CIMParameter(
                'Foo', type='string', embedded_object='instance',
                value=[CIMInstance('CIM_Foo')],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE EmbeddedObject="instance" NAME="Foo" '
                'PARAMTYPE="string">',
                '<VALUE.ARRAY>',
                '<VALUE>&lt;INSTANCE CLASSNAME=&quot;CIM_Foo&quot;/&gt;'
                '</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with embedded object type that is instance",
        dict(
            obj=CIMParameter(
                'Foo', type='string', embedded_object='object',
                value=[CIMInstance('CIM_Foo')],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE EmbeddedObject="object" NAME="Foo" '
                'PARAMTYPE="string">',
                '<VALUE.ARRAY>',
                '<VALUE>&lt;INSTANCE CLASSNAME=&quot;CIM_Foo&quot;/&gt;'
                '</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
    (
        "Array parameter as value with embedded object type that is class",
        dict(
            obj=CIMParameter(
                'Foo', type='string', embedded_object='object',
                value=[CIMClass('CIM_Foo')],
                is_array=True,
            ),
            kwargs=dict(as_value=True),
            exp_xml_str=(
                '<PARAMVALUE EmbeddedObject="object" NAME="Foo" '
                'PARAMTYPE="string">',
                '<VALUE.ARRAY>',
                '<VALUE>&lt;CLASS NAME=&quot;CIM_Foo&quot;/&gt;'
                '</VALUE>',
                '</VALUE.ARRAY>',
                '</PARAMVALUE>',
            )
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMPARAMETER_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMParameter_tocimxml(testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMParameter.tocimxml().
    """

    # The code to be tested
    obj_xml = obj.tocimxml(**kwargs)

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

    obj_xml_str = obj_xml.toxml()
    exp_xml_str = ''.join(exp_xml_str)
    validate_cim_xml_obj(obj, obj_xml_str, exp_xml_str)


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMPARAMETER_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMParameter_tocimxmlstr(testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMParameter.tocimxmlstr().
    """

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(**kwargs)

    # 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(obj_xml_str, str)

    exp_xml_str = ''.join(exp_xml_str)
    validate_cim_xml_obj(obj, obj_xml_str, exp_xml_str)


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMPARAMETER_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMParameter_tocimxmlstr_indent_int(
        testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMParameter.tocimxmlstr() with indent as integer.
    """

    indent = 4
    indent_str = ' ' * indent

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(indent=indent, **kwargs)

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

    obj_xml = obj.tocimxml(**kwargs)  # This is tested elsewhere
    exp_xml_str = obj_xml.toprettyxml(indent=indent_str)

    assert obj_xml_str == exp_xml_str, (
        f"{obj.__class__.__name__}.tocimxmlstr(indent={indent!r}) "
        "returns unexpected CIM-XML string")


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMPARAMETER_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMParameter_tocimxmlstr_indent_str(
        testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMParameter.tocimxmlstr() with indent as string.
    """

    indent = 4
    indent_str = ' ' * indent

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(indent=indent_str, **kwargs)

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

    obj_xml = obj.tocimxml(**kwargs)  # This is tested elsewhere
    exp_xml_str = obj_xml.toprettyxml(indent=indent_str)

    assert obj_xml_str == exp_xml_str, (
        f"{obj.__class__.__name__}.tocimxmlstr(indent={indent_str!r}) "
        "returns unexpected CIM-XML string")


TESTCASES_CIMPARAMETER_TOMOF = [

    # Testcases for CIMParameter.tomof()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: CIMParameter object to be tested.
    #   * kwargs: Dict of input args to tomof() method
    #   * exp_mof: Expected MOF result string, or None if failed.
    # * 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

    (
        "all components",
        dict(
            obj=CIMParameter(
                name='P1',
                type='string',
                reference_class=None,
                is_array=True,
                array_size=5,
                qualifiers=[
                    CIMQualifier('Q1', value='abc', type='string'),
                    CIMQualifier('Q2', value=Uint32(42), type='uint32'),
                ],
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
               [Q1 ( "abc" ),
                Q2 ( 42 )]
            string P1[5]""",
        ),
        None, None, True
    ),
    (
        "no qualifiers",
        dict(
            obj=CIMParameter(
                name='P1',
                type='string',
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
            string P1""",
        ),
        None, None, True
    ),
    (
        "one scalar single line qualifier",
        dict(
            obj=CIMParameter(
                name='P1',
                type='string',
                qualifiers=[
                    CIMQualifier('Q1', value='abc', type='string'),
                ],
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
               [Q1 ( "abc" )]
            string P1""",
        ),
        None, None, True
    ),
    (
        "one scalar multi line qualifier",
        dict(
            obj=CIMParameter(
                name='P1',
                type='string',
                qualifiers=[
                    CIMQualifier('Q1', value=('abc def ' * 10 + 'z'),
                                 type='string'),
                ],
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
               [Q1 (
                   "abc def abc def abc def abc def abc def abc def abc def "
                   "abc def abc def abc def z" )]
            string P1""",
        ),
        None, None, True
    ),
    (
        "two scalar single line qualifiers",
        dict(
            obj=CIMParameter(
                name='P1',
                type='string',
                qualifiers=[
                    CIMQualifier('Q1', value='abc', type='string'),
                    CIMQualifier('Q2', value=Uint32(42), type='uint32'),
                ],
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
               [Q1 ( "abc" ),
                Q2 ( 42 )]
            string P1""",
        ),
        None, None, True
    ),
    (
        "two scalar multi line qualifiers",
        dict(
            obj=CIMParameter(
                name='P1',
                type='string',
                qualifiers=[
                    CIMQualifier('Q1', value=('abc def ' * 10 + 'z'),
                                 type='string'),
                    CIMQualifier('Q2', value=('rst uvw ' * 10 + 'z'),
                                 type='string'),
                ],
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
               [Q1 (
                   "abc def abc def abc def abc def abc def abc def abc def "
                   "abc def abc def abc def z" ),
                Q2 (
                   "rst uvw rst uvw rst uvw rst uvw rst uvw rst uvw rst uvw "
                   "rst uvw rst uvw rst uvw z" )]
            string P1""",
        ),
        None, None, True
    ),
    (
        "one array single line qualifier",
        dict(
            obj=CIMParameter(
                name='P1',
                type='string',
                qualifiers=[
                    CIMQualifier('Q1', value=['abc', 'def'], type='string'),
                ],
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
               [Q1 { "abc", "def" }]
            string P1""",
        ),
        None, None, True
    ),
    (
        "one array multi line qualifier with short items",
        dict(
            obj=CIMParameter(
                name='P1',
                type='string',
                qualifiers=[
                    CIMQualifier(
                        'Q1',
                        value=[f'abcdef{_i:02d}' for _i in range(0, 10)],
                        type='string'),
                ],
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
               [Q1 { "abcdef00", "abcdef01", "abcdef02", "abcdef03",
                   "abcdef04", "abcdef05", "abcdef06", "abcdef07",
                   "abcdef08", "abcdef09" }]
            string P1""",
        ),
        None, None, True
    ),
    (
        "one array multi line qualifier with long items",
        dict(
            obj=CIMParameter(
                name='P1',
                type='string',
                qualifiers=[
                    CIMQualifier(
                        'Q1',
                        value=['abc def ' * 10 + f'z{_i:02d}'
                               for _i in range(0, 2)],
                        type='string'),
                ],
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
               [Q1 {
                   "abc def abc def abc def abc def abc def abc def abc def "
                   "abc def abc def abc def z00",
                   "abc def abc def abc def abc def abc def abc def abc def "
                   "abc def abc def abc def z01" }]
            string P1""",
        ),
        None, None, True
    ),
    (
        "string array parameter, variable size",
        dict(
            obj=CIMParameter(
                name='P1',
                type='string',
                is_array=True,
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
            string P1[]""",
        ),
        None, None, True
    ),
    (
        "uint32 array parameter, fixed size",
        dict(
            obj=CIMParameter(
                name='P1',
                type='uint32',
                is_array=True,
                array_size=5,
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
            uint32 P1[5]""",
        ),
        None, None, True
    ),
    (
        "reference parameter",
        dict(
            obj=CIMParameter(
                name='P1',
                type='reference',
                reference_class='RC',
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
            RC REF P1""",
        ),
        None, None, True
    ),
    (
        "reference array parameter, variable size",
        dict(
            obj=CIMParameter(
                name='P1',
                type='reference',
                reference_class='RC',
                is_array=True,
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
            RC REF P1[]""",
        ),
        None, None, True
    ),
    (
        "datetime parameter",
        dict(
            obj=CIMParameter(
                name='P1',
                type='datetime',
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
            datetime P1""",
        ),
        None, None, True
    ),
    (
        "boolean array parameter, fixed size",
        dict(
            obj=CIMParameter(
                name='P1',
                type='boolean',
                is_array=True,
                array_size=5,
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
            boolean P1[5]""",
        ),
        None, None, True
    ),
    (
        "real64 parameter",
        dict(
            obj=CIMParameter(
                name='P1',
                type='real64',
            ),
            kwargs=dict(
                indent=12,
            ),
            exp_mof="""\
            real64 P1""",
        ),
        None, None, True
    ),
    (
        "Parameter with name that does not fit onto line by 10",
        dict(
            obj=CIMParameter('Very_long_parameter_name', type='string'),
            kwargs=dict(
                indent=3,
                maxline=15,
            ),
            exp_mof='   string Very_long_parameter_name',
        ),
        None, None, True
    ),
    (
        "Parameter with qualifier value that does not fit onto line by 1",
        dict(
            obj=CIMParameter(
                'P1', type='string',
                qualifiers=[
                    CIMQualifier('Q1', value=Uint64(1998012513304050)),
                ]
            ),
            kwargs=dict(
                indent=3,
                maxline=28,
            ),
            exp_mof=None,
        ),
        ValueError, None, True
    ),
    (
        "Parameter with qualifier value that exactly fits onto line",
        dict(
            obj=CIMParameter(
                'P1', type='string',
                qualifiers=[
                    CIMQualifier('Q1', value=Uint64(1998012513304050)),
                ]
            ),
            kwargs=dict(
                indent=3,
                maxline=29,
            ),
            exp_mof=''
            '      [Q1 (\n'
            '          1998012513304050 )]\n'
            '   string P1',
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMPARAMETER_TOMOF)
@simplified_test_function
@log_entry_exit
def test_CIMParameter_tomof(testcase, obj, kwargs, exp_mof):
    """
    Test function for CIMParameter.tomof().
    """

    # The code to be tested
    mof = obj.tomof(**kwargs)

    # 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(mof, str)
    assert mof == exp_mof


TESTCASES_CIMQUALIFIERDECLARATION_INIT = [

    # Testcases for CIMQualifierDeclaration.__init__()

    # 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 CIMQualifierDeclaration().
    #   * init_kwargs: Dict of keyword arguments to CIMQualifierDeclaration().
    #   * exp_attrs: Dict of expected attributes of resulting 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

    # Order of positional arguments
    (
        "Verify order of positional arguments",
        dict(
            init_args=[
                'FooQual',
                'string',
                ['abc'],
                True,
                2,
                OrderedDict(CLASS=True),
                False,
                True,
                False,
                True,
            ],
            init_kwargs={},
            exp_attrs=dict(
                name='FooQual',
                type='string',
                value=['abc'],
                is_array=True,
                array_size=2,
                scopes=NocaseDict(CLASS=True),
                overridable=False,
                tosubclass=True,
                toinstance=False,
                translatable=True,
            )
        ),
        None, None, True
    ),

    # Type tests
    (
        "Verify that reference type fails",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooQual', type='reference', value=None),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that bytes name and return type are converted to unicode",
        dict(
            init_args=[],
            init_kwargs=dict(name=b'FooQual', type=b'string'),
            exp_attrs=dict(name='FooQual', type='string')
        ),
        None, None, True
    ),
    (
        "Verify that unicode name and return type remain unicode",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooQual', type='string'),
            exp_attrs=dict(name='FooQual', type='string')
        ),
        None, None, True
    ),
    (
        "Verify that float value without type fails",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooQual', type=None, value=42.0),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify float value with type real64",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooQual', type='real64', value=42.0),
            exp_attrs=dict(name='FooQual', type='real64', value=Real64(42.0))
        ),
        None, None, True
    ),
    (
        "Verify float value with type real32",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooQual', type='real32', value=42.0),
            exp_attrs=dict(name='FooQual', type='real32', value=Real32(42.0))
        ),
        None, None, True
    ),

    # Type tests with arrays
    (
        "Verify that is_array int 42 is converted to bool True",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                is_array=42
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                is_array=True
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array int 0 is converted to bool False",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                is_array=0
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                is_array=False
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array bool True remains True",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                is_array=True
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                is_array=True
            )
        ),
        None, None, True
    ),
    (
        "Verify that is_array bool False remains False",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                is_array=False
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                is_array=False
            )
        ),
        None, None, True
    ),
    (
        "Verify that unspecified is_array is implied to scalar by value "
        "None (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                is_array=None,
                value=None
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                is_array=False,
                value=None
            )
        ),
        None, None, True
    ),
    (
        "Verify that unspecified is_array is implied to scalar by scalar "
        "value (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                is_array=None,
                value='abc'
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                is_array=False,
                value='abc'
            )
        ),
        None, None, True
    ),
    (
        "Verify that unspecified is_array is implied to array by array "
        "value (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                is_array=None,
                value=['abc']
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                is_array=True,
                value=['abc']
            )
        ),
        None, None, True
    ),
    (
        "Verify that array_size int 42 remains int",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                is_array=True,
                array_size=42
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                is_array=True,
                array_size=42
            )
        ),
        None, None, True
    ),
    (
        "Verify that mismatch between is_array False and array_size is "
        "ignored",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                is_array=False,
                array_size=4
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                is_array=False,
                array_size=4
            )
        ),
        None, None, True
    ),

    # Scopes tests
    (
        "Verify that scopes dict is converted to NocaseDict",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                scopes=NocaseDict(CLASS=True)
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                scopes=NocaseDict(CLASS=True)
            )
        ),
        None, None, True
    ),

    # Overridable tests
    (
        "Verify that overridable int 42 is converted to bool True",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                overridable=42
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                overridable=True
            )
        ),
        None, None, True
    ),
    (
        "Verify that overridable int 0 is converted to bool False",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                overridable=0
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                overridable=False
            )
        ),
        None, None, True
    ),
    (
        "Verify that overridable bool True remains True",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                overridable=True
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                overridable=True
            )
        ),
        None, None, True
    ),
    (
        "Verify that overridable bool False remains False",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                overridable=False
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                overridable=False
            )
        ),
        None, None, True
    ),

    # Tosubclass tests
    (
        "Verify that tosubclass int 42 is converted to bool True",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                tosubclass=42
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                tosubclass=True
            )
        ),
        None, None, True
    ),
    (
        "Verify that tosubclass int 0 is converted to bool False",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                tosubclass=0
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                tosubclass=False
            )
        ),
        None, None, True
    ),
    (
        "Verify that tosubclass bool True remains True",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                tosubclass=True
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                tosubclass=True
            )
        ),
        None, None, True
    ),
    (
        "Verify that tosubclass bool False remains False",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                tosubclass=False
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                tosubclass=False
            )
        ),
        None, None, True
    ),

    # Toinstance tests
    (
        "Verify that toinstance int 42 is converted to bool True",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                toinstance=42
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                toinstance=True
            )
        ),
        None, None, True
    ),
    (
        "Verify that toinstance int 0 is converted to bool False",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                toinstance=0
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                toinstance=False
            )
        ),
        None, None, True
    ),
    (
        "Verify that toinstance bool True remains True",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                toinstance=True
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                toinstance=True
            )
        ),
        None, None, True
    ),
    (
        "Verify that toinstance bool False remains False",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                toinstance=False
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                toinstance=False
            )
        ),
        None, None, True
    ),

    # Translatable tests
    (
        "Verify that translatable int 42 is converted to bool True",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                translatable=42
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                translatable=True
            )
        ),
        None, None, True
    ),
    (
        "Verify that translatable int 0 is converted to bool False",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                translatable=0
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                translatable=False
            )
        ),
        None, None, True
    ),
    (
        "Verify that translatable bool True remains True",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                translatable=True
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                translatable=True
            )
        ),
        None, None, True
    ),
    (
        "Verify that translatable bool False remains False",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='string',
                translatable=False
            ),
            exp_attrs=dict(
                name='FooQual',
                type='string',
                translatable=False
            )
        ),
        None, None, True
    ),

    # Exception testcases
    (
        "Verify that name None fails (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name=None, type='string'),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that type None fails (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooQual', type=None),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that invalid type fails (since 0.12)",
        dict(
            init_args=[],
            init_kwargs=dict(name='FooQual', type='xxx'),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that mismatch between is_array False and array value fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='uint32',
                is_array=False,
                value=[Uint32(x) for x in [1, 2, 3]]
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
    (
        "Verify that mismatch between is_array True and scalar value fails",
        dict(
            init_args=[],
            init_kwargs=dict(
                name='FooQual',
                type='uint32',
                is_array=True,
                value=Uint32(3)
            ),
            exp_attrs=None
        ),
        ValueError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMQUALIFIERDECLARATION_INIT)
@simplified_test_function
@log_entry_exit
def test_CIMQualifierDeclaration_init(
        testcase, init_args, init_kwargs, exp_attrs):
    """
    Test function for CIMQualifierDeclaration.__init__()
    """

    # The code to be tested
    obj = CIMQualifierDeclaration(*init_args, **init_kwargs)

    assert not hasattr(obj, '__dict__')

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

    exp_name = exp_attrs['name']
    assert obj.name == exp_name
    assert isinstance(obj.name, type(exp_name))

    exp_type = exp_attrs['type']
    assert obj.type == exp_type
    assert isinstance(obj.type, type(exp_type))

    exp_value = exp_attrs.get('value', None)
    assert obj.value == exp_value
    assert isinstance(obj.value, type(exp_value))

    exp_is_array = exp_attrs.get('is_array', False)
    assert obj.is_array == exp_is_array
    assert isinstance(obj.is_array, type(exp_is_array))

    exp_array_size = exp_attrs.get('array_size', None)
    assert obj.array_size == exp_array_size
    assert isinstance(obj.array_size, type(exp_array_size))

    exp_scopes = exp_attrs.get('scopes', NocaseDict())
    assert obj.scopes == exp_scopes
    assert isinstance(obj.scopes, type(exp_scopes))

    exp_overridable = exp_attrs.get('overridable', None)
    assert obj.overridable == exp_overridable
    assert isinstance(obj.overridable, type(exp_overridable))

    exp_tosubclass = exp_attrs.get('tosubclass', None)
    assert obj.tosubclass == exp_tosubclass
    assert isinstance(obj.tosubclass, type(exp_tosubclass))

    exp_toinstance = exp_attrs.get('toinstance', None)
    assert obj.toinstance == exp_toinstance
    assert isinstance(obj.toinstance, type(exp_toinstance))

    exp_translatable = exp_attrs.get('translatable', None)
    assert obj.translatable == exp_translatable
    assert isinstance(obj.translatable, type(exp_translatable))


TESTCASES_CIMQUALIFIERDECLARATION_COPY = [

    # Testcases for CIMQualifierDeclaration.copy()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj_kwargs: Dict of init kwargs for original CIMQualifierDeclaration.
    # * 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

    (
        "Qualifier of type scalar string, with all boolean attributes True, "
        "no scopes",
        dict(
            obj_kwargs=dict(
                name='PropFoo',
                value='Bar',
                type='string',
                is_array=False,
                array_size=None,
                overridable=True,
                tosubclass=True,
                toinstance=True,
                translatable=True,
                scopes=None,
            )
        ),
        None, None, True
    ),
    (
        "Qualifier of type variable array datetime, with all boolean "
        "attributes False, no scopes",
        dict(
            obj_kwargs=dict(
                name='PropFoo',
                value=['20191005155152.123456-120'],
                type='datetime',
                is_array=True,
                array_size=None,
                overridable=False,
                tosubclass=False,
                toinstance=False,
                translatable=False,
                scopes=None,
            )
        ),
        None, None, True
    ),
    (
        "Qualifier of type fixed array uint8, with all boolean "
        "attributes unset, with scopes",
        dict(
            obj_kwargs=dict(
                name='PropFoo',
                value=[42],
                type='uint8',
                is_array=True,
                array_size=5,
                overridable=None,
                tosubclass=None,
                toinstance=None,
                translatable=None,
                scopes=NocaseDict([
                    ('ANY', True),
                    ('CLASS', False),
                    ('ASSOCIATION', False),
                ]),
            )
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMQUALIFIERDECLARATION_COPY)
@simplified_test_function
@log_entry_exit
def test_CIMQualifierDeclaration_copy(testcase, obj_kwargs):
    """
    Test function for CIMQualifierDeclaration.copy()
    """

    obj1 = CIMQualifierDeclaration(**obj_kwargs)

    # The code to be tested
    obj2 = obj1.copy()

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

    # Verify that the copy is equal to the original (performs deep equality)
    assert obj2 == obj1

    # Verify that the copy is a different object
    assert id(obj2) != id(obj1)

    # TODO: Decide whether mutable child objects should be copied, and add test:
    # # Verify that the mutable child objects are different objects
    # if not isinstance(obj1.value, (int, str)):
    #     assert id(obj2.value) != id(obj1.value)

    # Verify that the copy can be modified and the original remains unchanged.
    # Most of the attribute setters don't validate the change, because multiple
    # such changes might be needed to make the object consistent again.

    obj1_name = obj1.name
    obj2.name = 'SomeNewQual'
    assert obj1.name == obj1_name

    # TODO: Decide whether value objects should be copied, and add test:
    # obj1_value = obj1.value
    # obj2.value = None  # avoid going through the types
    # assert obj1.value == obj1_value

    obj1_type = obj1.type
    obj2.type = 'uint8' if obj1.type == 'string' else 'string'
    assert obj1.type == obj1_type

    obj1_is_array = obj1.is_array
    obj2.is_array = not obj1.is_array
    assert obj1.is_array == obj1_is_array

    if obj1.is_array:
        obj1_array_size = obj1.array_size
        obj2.array_size = 10 if obj1.array_size is None else None
        assert obj1.array_size == obj1_array_size

    obj1_overridable = obj1.overridable
    obj2.overridable = not obj1.overridable
    assert obj1.overridable == obj1_overridable

    obj1_tosubclass = obj1.tosubclass
    obj2.tosubclass = not obj1.tosubclass
    assert obj1.tosubclass == obj1_tosubclass

    obj1_toinstance = obj1.toinstance
    obj2.toinstance = not obj1.toinstance
    assert obj1.toinstance == obj1_toinstance

    obj1_translatable = obj1.translatable
    obj2.translatable = not obj1.translatable
    assert obj1.translatable == obj1_translatable

    obj1_scopes = obj1.scopes
    obj2.scopes = dict(ANY=False) if obj1.scopes is None else None
    assert obj1.scopes == obj1_scopes


CIMQUALIFIERDECLARATION_SETATTR_Q1_KWARGS = dict(
    name='Q1',
    value='V1',
    type='string',
    is_array=False,
    array_size=None,
    scopes=OrderedDict(ANY=True),
    overridable=None,
    tosubclass=None,
    toinstance=None,
    translatable=None,
)

TESTCASES_CIMQUALIFIERDECLARATION_SETATTR = [

    # Testcases for CIMQualifierDeclaration set attribute

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj_kwargs: Dict of init kwargs for CIMQualifierDeclaration.
    #   * item: Name of CIMQualifierDeclaration attr to set, or tuple
    #     (item, key) to set a single item in an attr that is a dict.
    #   * new_value: New value to set.
    #   * exp_attrs: Dict of expected attributes, for validation.
    # * 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

    # Tests that set the name attribute
    (
        "Set name to different string",
        dict(
            obj_kwargs=CIMQUALIFIERDECLARATION_SETATTR_Q1_KWARGS,
            item='name',
            new_value='Q2',
            exp_attrs=dict(
                name='Q2',
            ),
        ),
        None, None, True
    ),
    (
        "Set name to None (raises ValueError since 0.12)",
        dict(
            obj_kwargs=CIMQUALIFIERDECLARATION_SETATTR_Q1_KWARGS,
            item='name',
            new_value=None,
            exp_attrs=dict(
                name=None,
            ),
        ),
        ValueError, None, True
    ),

    # Tests that set the value attribute for scalar string types
    (
        "For scalar string type, set value to 7-bit ASCII unicode string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
            ),
            item='value',
            new_value='V2',
            exp_attrs=dict(
                value='V2',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set value to 7-bit ASCII byte string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
            ),
            item='value',
            new_value=b'V2',
            exp_attrs=dict(
                value='V2',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set value to non-UCS-2 unicode string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
            ),
            item='value',
            new_value='Foo\U00010142',
            exp_attrs=dict(
                value='Foo\U00010142',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set value to non-UCS-2 UTF-8 byte string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
            ),
            item='value',
            new_value=b'Foo\xF0\x90\x85\x82',
            exp_attrs=dict(
                value='Foo\U00010142',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set value to None",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for scalar char16 types
    (
        "For scalar char16 type, set value to 7-bit ASCII Char16 string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value=Char16('U'),
            exp_attrs=dict(
                value='U',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar char16 type, set value to 7-bit ASCII unicode string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value='U',
            exp_attrs=dict(
                value='U',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar char16 type, set value to 7-bit ASCII byte string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value=b'U',
            exp_attrs=dict(
                value='U',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar char16 type, set value to non-UCS-2 Char16 string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value=Char16('\U00010142'),
            exp_attrs=dict(
                value='\U00010142',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar char16 type, set value to non-UCS-2 unicode string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value='\U00010142',
            exp_attrs=dict(
                value='\U00010142',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar char16 type, set value to non-UCS-2 UTF-8 byte string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value=b'\xF0\x90\x85\x82',
            exp_attrs=dict(
                value='\U00010142',
            ),
        ),
        None, None, True
    ),
    (
        "For scalar char16 type, set value to None",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='X',
                type='char16',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for scalar boolean type
    (
        "For scalar boolean type, set value to boolean True",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=False,
                type='boolean',
            ),
            item='value',
            new_value=True,
            exp_attrs=dict(
                value=True,
            ),
        ),
        None, None, True
    ),
    (
        "For scalar boolean type, set value to boolean False",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=True,
                type='boolean',
            ),
            item='value',
            new_value=False,
            exp_attrs=dict(
                value=False,
            ),
        ),
        None, None, True
    ),
    (
        "For scalar boolean type, set value to string 'true'",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=False,
                type='boolean',
            ),
            item='value',
            new_value='true',
            exp_attrs=dict(
                value=True,
            ),
        ),
        None, None, True
    ),
    (
        "For scalar boolean type, set value to string 'false'",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=False,
                type='boolean',
            ),
            item='value',
            new_value='false',
            exp_attrs=dict(
                value=True,  # no processing of 'true'/'false' strings
            ),
        ),
        None, None, True
    ),
    (
        "For scalar boolean type, set value to None",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=False,
                type='boolean',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for scalar integer types
    (
        "For scalar uint8 type, set value to integer out of range",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=42,
                type='uint8',
            ),
            item='value',
            new_value=256,
            exp_attrs=dict(
                value=None,
            ),
        ),
        ValueError, None, True  # out of range
    ),
    (
        "For scalar uint64 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=42,
                type='uint64',
            ),
            item='value',
            new_value=12345678901234,
            exp_attrs=dict(
                value=12345678901234,
            ),
        ),
        None, None, True
    ),
    (
        "For scalar sint8 type, set value to integer out of range",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=42,
                type='sint8',
            ),
            item='value',
            new_value=-129,
            exp_attrs=dict(
                value=None,
            ),
        ),
        ValueError, None, True  # out of range
    ),
    (
        "For scalar sint64 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=42,
                type='sint64',
            ),
            item='value',
            new_value=-12345678901234,
            exp_attrs=dict(
                value=-12345678901234,
            ),
        ),
        None, None, True
    ),
    (
        "For scalar sint32 type, set value to None",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=42,
                type='sint32',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for scalar real types
    (
        "For scalar real32 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=42.1,
                type='real32',
            ),
            item='value',
            new_value=-12345678890.1,
            exp_attrs=dict(
                value=-12345678890.1,
            ),
        ),
        None, None, True
    ),
    (
        "For scalar real64 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=42.1,
                type='real64',
            ),
            item='value',
            new_value=-12345678890.1,
            exp_attrs=dict(
                value=-12345678890.1,
            ),
        ),
        None, None, True
    ),
    (
        "For scalar real64 type, set value to None",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=42,
                type='real64',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for scalar datetime type
    (
        "For scalar datetime type, set value to datetime object",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value=datetime(
                year=2019, month=10, day=20, hour=15, minute=30, second=40,
                microsecond=654321, tzinfo=MinutesFromUTC(120)
            ),
            exp_attrs=dict(
                value=CIMDateTime(datetime(
                    year=2019, month=10, day=20, hour=15, minute=30, second=40,
                    microsecond=654321, tzinfo=MinutesFromUTC(120)
                )),
            ),
        ),
        None, None, True
    ),
    (
        "For scalar datetime type, set value to point in time string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value='20191020153040.654321+120',
            exp_attrs=dict(
                value=CIMDateTime('20191020153040.654321+120'),
            ),
        ),
        None, None, True
    ),
    (
        "For scalar datetime type, set value to point in time CIMDateTime",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value=CIMDateTime('20191020153040.654321+120'),
            exp_attrs=dict(
                value=CIMDateTime('20191020153040.654321+120'),
            ),
        ),
        None, None, True
    ),
    (
        "For scalar datetime type, set value to timedelta object",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value=timedelta(25, (14 * 60 + 24) * 60 + 41, 234567),
            exp_attrs=dict(
                value=CIMDateTime(
                    timedelta(25, (14 * 60 + 24) * 60 + 41, 234567)
                ),
            ),
        ),
        None, None, True
    ),
    (
        "For scalar datetime type, set value to interval string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value='00000173232441.234567:000',
            exp_attrs=dict(
                value=CIMDateTime('00000173232441.234567:000'),
            ),
        ),
        None, None, True
    ),
    (
        "For scalar datetime type, set value to interval CIMDateTime",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value=CIMDateTime('00000173232441.234567:000'),
            exp_attrs=dict(
                value=CIMDateTime('00000173232441.234567:000'),
            ),
        ),
        None, None, True
    ),
    (
        "For scalar datetime type, set value to None",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=DATETIME1_OBJ,
                type='datetime',
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for scalar embedded objects
    (
        "For scalar string type, set value to embedded instance",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='X',
                type='string',
            ),
            item='value',
            new_value=CIMInstance('C2'),
            exp_attrs=dict(
                value=CIMInstance('C2'),
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set value to embedded class",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='X',
                type='string',
            ),
            item='value',
            new_value=CIMClass('C2'),
            exp_attrs=dict(
                value=CIMClass('C2'),
            ),
        ),
        None, None, True
    ),

    # Tests that set array value on scalar property and vice versa
    (
        "For array string type, set value to scalar string",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value='V2',
            exp_attrs=dict(
                value='V2',  # scalar/array mismatch not checked
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set value to array of strings",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
            ),
            item='value',
            new_value=['V2'],
            exp_attrs=dict(
                value=['V2'],  # scalar/array mismatch not checked
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for array string types
    (
        "For array string type, set value to array of 7-bit ASCII unicode "
        "strings",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['V2', 'V3'],
            exp_attrs=dict(
                value=['V2', 'V3'],
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set value to array of 7-bit ASCII byte "
        "strings",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[b'V2', b'V3'],
            exp_attrs=dict(
                value=['V2', 'V3'],
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set value to array of non-UCS-2 unicode "
        "strings",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['Foo\U00010142'],
            exp_attrs=dict(
                value=['Foo\U00010142'],
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set value to array of non-UCS-2 UTF-8 byte "
        "strings",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[b'Foo\xF0\x90\x85\x82'],
            exp_attrs=dict(
                value=['Foo\U00010142'],
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set value to empty array",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[],
            exp_attrs=dict(
                value=[],
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set value to array of one None item",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[None],
            exp_attrs=dict(
                value=[None],
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set value to None",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),
    (
        "For fixed array string type, set value to max items",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=2,
            ),
            item='value',
            new_value=['V1', 'V2'],
            exp_attrs=dict(
                value=['V1', 'V2'],
            ),
        ),
        None, None, True
    ),
    (
        "For fixed array string type, set value to one more than max items",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=2,
            ),
            item='value',
            new_value=['V1', 'V2', 'V3'],
            exp_attrs=dict(
                value=['V1', 'V2', 'V3'],  # fixed size not checked
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for array char16 types
    (
        "For array char16 type, set value to array of 7-bit ASCII Char16 "
        "strings",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[Char16('U'), Char16('V')],
            exp_attrs=dict(
                value=['U', 'V'],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to array of 7-bit ASCII unicode "
        "strings",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['U', 'V'],
            exp_attrs=dict(
                value=['U', 'V'],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to array of 7-bit ASCII byte "
        "strings",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[b'U', b'V'],
            exp_attrs=dict(
                value=['U', 'V'],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to array of non-UCS-2 Char16 "
        "strings",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[Char16('\U00010142')],
            exp_attrs=dict(
                value=['\U00010142'],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to array of non-UCS-2 unicode "
        "strings",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['\U00010142'],
            exp_attrs=dict(
                value=['\U00010142'],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to array of non-UCS-2 UTF-8 byte "
        "strings",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[b'\xF0\x90\x85\x82'],
            exp_attrs=dict(
                value=['\U00010142'],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to empty array",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[],
            exp_attrs=dict(
                value=[],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to array of one None item",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[None],
            exp_attrs=dict(
                value=[None],
            ),
        ),
        None, None, True
    ),
    (
        "For array char16 type, set value to None",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['X'],
                type='char16',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for array boolean type
    (
        "For array boolean type, set value to array of boolean True",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[False],
                type='boolean',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[True, False],
            exp_attrs=dict(
                value=[True, False],
            ),
        ),
        None, None, True
    ),
    (
        "For array boolean type, set value to array of boolean False",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[True],
                type='boolean',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[False, True],
            exp_attrs=dict(
                value=[False, True],
            ),
        ),
        None, None, True
    ),
    (
        "For array boolean type, set value to array of string 'true'",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[False],
                type='boolean',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['true'],
            exp_attrs=dict(
                value=[True],
            ),
        ),
        None, None, True
    ),
    (
        "For array boolean type, set value to array of string 'false'",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[False],
                type='boolean',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['false'],
            exp_attrs=dict(
                value=[True],  # no processing of 'true'/'false' strings
            ),
        ),
        None, None, True
    ),
    (
        "For array boolean type, set value to empty array",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[False],
                type='boolean',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[],
            exp_attrs=dict(
                value=[],
            ),
        ),
        None, None, True
    ),
    (
        "For array boolean type, set value to array of one None item",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[False],
                type='boolean',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[None],
            exp_attrs=dict(
                value=[None],
            ),
        ),
        None, None, True
    ),
    (
        "For array boolean type, set value to None",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[False],
                type='boolean',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for array integer types
    (
        "For array uint8 type, set value to integer out of range",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[42],
                type='uint8',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[256],
            exp_attrs=None,
        ),
        ValueError, None, True  # out of range
    ),
    (
        "For array uint64 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[42],
                type='uint64',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[12345678901234],
            exp_attrs=dict(
                value=[12345678901234],
            ),
        ),
        None, None, True
    ),
    (
        "For array sint8 type, set value to integer out of range",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[42],
                type='sint8',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[-129],
            exp_attrs=None,
        ),
        ValueError, None, True  # out of range
    ),
    (
        "For array sint64 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[42],
                type='sint64',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[-12345678901234],
            exp_attrs=dict(
                value=[-12345678901234],
            ),
        ),
        None, None, True
    ),
    (
        "For array sint32 type, set value to empty array",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[42],
                type='sint32',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[],
            exp_attrs=dict(
                value=[],
            ),
        ),
        None, None, True
    ),
    (
        "For array sint32 type, set value to array of one None item",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[42],
                type='sint32',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[None],
            exp_attrs=dict(
                value=[None],
            ),
        ),
        None, None, True
    ),
    (
        "For array sint32 type, set value to None",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[42],
                type='sint32',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for array real types
    (
        "For array real32 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[42.1],
                type='real32',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[-12345678890.1],
            exp_attrs=dict(
                value=[-12345678890.1],
            ),
        ),
        None, None, True
    ),
    (
        "For array real64 type, set value to integer in range",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[42.1],
                type='real64',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[-12345678890.1],
            exp_attrs=dict(
                value=[-12345678890.1],
            ),
        ),
        None, None, True
    ),
    (
        "For array real64 type, set value to empty array",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[42],
                type='real64',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[],
            exp_attrs=dict(
                value=[],
            ),
        ),
        None, None, True
    ),
    (
        "For array real64 type, set value to array of one None item",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[42],
                type='real64',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[None],
            exp_attrs=dict(
                value=[None],
            ),
        ),
        None, None, True
    ),
    (
        "For array real64 type, set value to None",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[42],
                type='real64',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Note: References cannot be arrays - tests are in test_CIMProperty_init()

    # Tests that set the value attribute for array datetime type
    (
        "For array datetime type, set value to array of datetime objects",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[datetime(
                year=2019, month=10, day=20, hour=15, minute=30, second=40,
                microsecond=654321, tzinfo=MinutesFromUTC(120)
            )],
            exp_attrs=dict(
                value=[CIMDateTime(datetime(
                    year=2019, month=10, day=20, hour=15, minute=30, second=40,
                    microsecond=654321, tzinfo=MinutesFromUTC(120)
                ))],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to array of point in time strings",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['20191020153040.654321+120'],
            exp_attrs=dict(
                value=[CIMDateTime('20191020153040.654321+120')],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to array of point in time "
        "CIMDateTime objects",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[CIMDateTime('20191020153040.654321+120')],
            exp_attrs=dict(
                value=[CIMDateTime('20191020153040.654321+120')],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to array of timedelta objects",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[timedelta(25, (14 * 60 + 24) * 60 + 41, 234567)],
            exp_attrs=dict(
                value=[CIMDateTime(
                    timedelta(25, (14 * 60 + 24) * 60 + 41, 234567)
                )],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to array of interval strings",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=['00000173232441.234567:000'],
            exp_attrs=dict(
                value=[CIMDateTime('00000173232441.234567:000')],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to array of interval CIMDateTime"
        "objects",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[CIMDateTime('00000173232441.234567:000')],
            exp_attrs=dict(
                value=[CIMDateTime('00000173232441.234567:000')],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to empty array",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[],
            exp_attrs=dict(
                value=[],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to array of one None item",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[None],
            exp_attrs=dict(
                value=[None],
            ),
        ),
        None, None, True
    ),
    (
        "For array datetime type, set value to None",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=[DATETIME1_OBJ],
                type='datetime',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=None,
            exp_attrs=dict(
                value=None,
            ),
        ),
        None, None, True
    ),

    # Tests that set the value attribute for array embedded objects
    (
        "For array string type, set value to embedded instance",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['X'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[CIMInstance('C2')],
            exp_attrs=dict(
                value=[CIMInstance('C2')],
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set value to embedded class",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['X'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='value',
            new_value=[CIMClass('C2')],
            exp_attrs=dict(
                value=[CIMClass('C2')],
            ),
        ),
        None, None, True
    ),

    # Tests that set the type attribute
    (
        "For scalar string type, set type to uint8",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
            ),
            item='type',
            new_value='uint8',
            exp_attrs=dict(
                type='uint8',
                value='V1',
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set type to uint8",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='type',
            new_value='uint8',
            exp_attrs=dict(
                type='uint8',
                value=['V1'],
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set type to invalid type",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
            ),
            item='type',
            new_value='xxx',
            exp_attrs=None,
        ),
        ValueError, None, True  # invalid type
    ),
    (
        "For scalar string type, set type to None",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
            ),
            item='type',
            new_value=None,
            exp_attrs=None,
        ),
        ValueError, None, True  # invalid type
    ),

    # Tests that set the type attribute
    (
        "For string type, set type to uint8",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
            ),
            item='type',
            new_value='uint8',
            exp_attrs=dict(
                type='uint8',
                value='V1',
            ),
        ),
        None, None, True
    ),
    (
        "For string type, set type to invalid type",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
            ),
            item='type',
            new_value='xxx',
            exp_attrs=None,
        ),
        ValueError, None, True  # invalid type
    ),
    (
        "For string type, set type to None (raises ValueError since 0.12)",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
            ),
            item='type',
            new_value=None,
            exp_attrs=None,
        ),
        ValueError, None, True  # invalid type
    ),

    # Tests that set the is_array attribute
    (
        "For scalar string type, set is_array to True",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                is_array=False,
                array_size=None,
            ),
            item='is_array',
            new_value=True,
            exp_attrs=dict(
                type='string',
                value='V1',
                is_array=True,  # inconsistency with value not checked
            ),
        ),
        None, None, True
    ),
    (
        "For scalar string type, set is_array to 'false' "
        "(for boolean conversion to True)",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                is_array=False,
                array_size=None,
            ),
            item='is_array',
            new_value='false',
            exp_attrs=dict(
                type='string',
                value='V1',
                is_array=True,  # inconsistency with value not checked
            ),
        ),
        None, None, True
    ),
    (
        "For array string type, set is_array to False",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='is_array',
            new_value=False,
            exp_attrs=dict(
                type='string',
                value=['V1'],
                is_array=False,  # inconsistency with value not checked
            ),
        ),
        None, None, True
    ),

    # Tests that set the array_size attribute
    (
        "For scalar string type, set array_size to 2",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                is_array=False,
                array_size=None,
            ),
            item='array_size',
            new_value=2,
            exp_attrs=dict(
                type='string',
                value='V1',
                is_array=False,
                array_size=2,  # inconsistency with is_array not checked
            ),
        ),
        None, None, True
    ),
    (
        "For variable array string type, set array_size to 2",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=None,
            ),
            item='array_size',
            new_value=2,
            exp_attrs=dict(
                type='string',
                value=['V1'],
                is_array=True,
                array_size=2,
            ),
        ),
        None, None, True
    ),
    (
        "For fixed size 5 array string type with one item, set array_size to "
        "smaller size 2",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['V1'],
                type='string',
                is_array=True,
                array_size=5,
            ),
            item='array_size',
            new_value=2,
            exp_attrs=dict(
                type='string',
                value=['V1'],
                is_array=True,
                array_size=2,
            ),
        ),
        None, None, True
    ),
    (
        "For fixed size 5 array string type with 5 items, set array_size to "
        "too small size 2",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value=['V1', 'V2', 'V3', 'V4', 'V5'],
                type='string',
                is_array=True,
                array_size=5,
            ),
            item='array_size',
            new_value=2,
            exp_attrs=dict(
                type='string',
                value=['V1', 'V2', 'V3', 'V4', 'V5'],
                is_array=True,
                array_size=2,  # inconsistency with actual usage not checked
            ),
        ),
        None, None, True
    ),

    # Tests that set the scopes attribute
    (
        "Set entire scopes attribute to dict with CLASS,ASSOCIATION scope",
        dict(
            obj_kwargs=dict(
                name='Q1',
                type='string',
                scopes=OrderedDict(ANY=True),
            ),
            item='scopes',
            new_value=NocaseDict([('CLASS', True), ('ASSOCIATION', True)]),
            exp_attrs=dict(
                scopes=NocaseDict([('CLASS', True), ('ASSOCIATION', True)]),
            ),
        ),
        None, None, True
    ),
    (
        "Set entire scopes attribute to dict that is empty",
        dict(
            obj_kwargs=dict(
                name='Q1',
                type='string',
                scopes=OrderedDict(ANY=True),
            ),
            item='scopes',
            new_value=OrderedDict(),
            exp_attrs=dict(
                scopes={},
            ),
        ),
        None, None, True
    ),
    (
        "Set entire scopes attribute to dict that contains invalid scope",
        dict(
            obj_kwargs=dict(
                name='Q1',
                type='string',
                scopes=OrderedDict(ANY=True),
            ),
            item='scopes',
            new_value=OrderedDict(XXX=True),
            exp_attrs=dict(
                scopes=dict(XXX=True),  # validity not checked
            ),
        ),
        None, None, True
    ),
    (
        "Set entire scopes attribute to None",
        dict(
            obj_kwargs=dict(
                name='Q1',
                type='string',
                scopes=OrderedDict(ANY=True),
            ),
            item='scopes',
            new_value=None,
            exp_attrs=dict(
                scopes={},
            ),
        ),
        None, None, True
    ),
    (
        "Set a non-existing scope in scopes attribute to True",
        dict(
            obj_kwargs=dict(
                name='Q1',
                type='string',
                scopes=OrderedDict(CLASS=True),
            ),
            item=('scopes', 'ASSOCIATION'),
            new_value=True,
            exp_attrs=dict(
                scopes=dict(CLASS=True, ASSOCIATION=True),
            ),
        ),
        None, None, True
    ),
    (
        "Set an existing scope in scopes attribute from True to False",
        dict(
            obj_kwargs=dict(
                name='Q1',
                type='string',
                scopes=OrderedDict(CLASS=True),
            ),
            item=('scopes', 'CLASS'),
            new_value=False,
            exp_attrs=dict(
                scopes=dict(CLASS=False),
            ),
        ),
        None, None, True
    ),
    (
        "Set an invalid scope in scopes attribute",
        dict(
            obj_kwargs=dict(
                name='Q1',
                type='string',
                scopes=OrderedDict(CLASS=True),
            ),
            item=('scopes', 'XXX'),
            new_value=True,
            exp_attrs=dict(
                scopes=dict(CLASS=True, XXX=True),  # validity not checked
            ),
        ),
        None, None, True
    ),

    # Tests that set the overridable attribute
    (
        "Set overridable from False to True",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                overridable=False,
            ),
            item='overridable',
            new_value=True,
            exp_attrs=dict(
                overridable=True,
            ),
        ),
        None, None, True
    ),
    (
        "Set overridable from True to False",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                overridable=True,
            ),
            item='overridable',
            new_value=False,
            exp_attrs=dict(
                overridable=False,
            ),
        ),
        None, None, True
    ),
    (
        "Set overridable from False to 'false' (boolean conversion to True)",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                overridable=False,
            ),
            item='overridable',
            new_value='false',
            exp_attrs=dict(
                overridable=True,
            ),
        ),
        None, None, True
    ),
    (
        "Set overridable from False to 'true' (boolean conversion to True)",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                overridable=False,
            ),
            item='overridable',
            new_value='true',
            exp_attrs=dict(
                overridable=True,
            ),
        ),
        None, None, True
    ),

    # Tests that set the tosubclass attribute
    (
        "Set tosubclass from False to True",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                tosubclass=False,
            ),
            item='tosubclass',
            new_value=True,
            exp_attrs=dict(
                tosubclass=True,
            ),
        ),
        None, None, True
    ),
    (
        "Set tosubclass from True to False",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                tosubclass=True,
            ),
            item='tosubclass',
            new_value=False,
            exp_attrs=dict(
                tosubclass=False,
            ),
        ),
        None, None, True
    ),
    (
        "Set tosubclass from False to 'false' (boolean conversion to True)",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                tosubclass=False,
            ),
            item='tosubclass',
            new_value='false',
            exp_attrs=dict(
                tosubclass=True,
            ),
        ),
        None, None, True
    ),
    (
        "Set tosubclass from False to 'true' (boolean conversion to True)",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                tosubclass=False,
            ),
            item='tosubclass',
            new_value='true',
            exp_attrs=dict(
                tosubclass=True,
            ),
        ),
        None, None, True
    ),

    # Tests that set the toinstance attribute
    (
        "Set toinstance from False to True",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                toinstance=False,
            ),
            item='toinstance',
            new_value=True,
            exp_attrs=dict(
                toinstance=True,
            ),
        ),
        None, None, True
    ),
    (
        "Set toinstance from True to False",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                toinstance=True,
            ),
            item='toinstance',
            new_value=False,
            exp_attrs=dict(
                toinstance=False,
            ),
        ),
        None, None, True
    ),
    (
        "Set toinstance from False to 'false' (boolean conversion to True)",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                toinstance=False,
            ),
            item='toinstance',
            new_value='false',
            exp_attrs=dict(
                toinstance=True,
            ),
        ),
        None, None, True
    ),
    (
        "Set toinstance from False to 'true' (boolean conversion to True)",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                toinstance=False,
            ),
            item='toinstance',
            new_value='true',
            exp_attrs=dict(
                toinstance=True,
            ),
        ),
        None, None, True
    ),

    # Tests that set the translatable attribute
    (
        "Set translatable from False to True",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                translatable=False,
            ),
            item='translatable',
            new_value=True,
            exp_attrs=dict(
                translatable=True,
            ),
        ),
        None, None, True
    ),
    (
        "Set translatable from True to False",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                translatable=True,
            ),
            item='translatable',
            new_value=False,
            exp_attrs=dict(
                translatable=False,
            ),
        ),
        None, None, True
    ),
    (
        "Set translatable from False to 'false' (boolean conversion to True)",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                translatable=False,
            ),
            item='translatable',
            new_value='false',
            exp_attrs=dict(
                translatable=True,
            ),
        ),
        None, None, True
    ),
    (
        "Set translatable from False to 'true' (boolean conversion to True)",
        dict(
            obj_kwargs=dict(
                name='Q1',
                value='V1',
                type='string',
                translatable=False,
            ),
            item='translatable',
            new_value='true',
            exp_attrs=dict(
                translatable=True,
            ),
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMQUALIFIERDECLARATION_SETATTR)
@simplified_test_function
@log_entry_exit
def test_CIMQualifierDeclaration_setattr(
        testcase, obj_kwargs, item, new_value, exp_attrs):
    """
    Test function for CIMQualifierDeclaration set attribute
    """

    obj = CIMQualifierDeclaration(**obj_kwargs)

    if isinstance(item, tuple):
        attr_name, attr_key = item
        attr_dict = getattr(obj, attr_name)

        # The code to be tested (for setting a single dict item)
        attr_dict[attr_key] = new_value

    else:
        attr_name = item

        # The code to be tested (for setting the entire attribute)
        setattr(obj, attr_name, new_value)

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

    for attr_name in exp_attrs:
        exp_value = exp_attrs[attr_name]
        value = getattr(obj, attr_name)
        assert value == exp_value


TESTCASES_CIMQUALIFIERDECLARATION_HASH_EQ = [

    # Testcases for CIMQualifierDeclaration.__hash__() and __eq__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: CIMQualifierDeclaration object #1 to be tested.
    #   * obj2: CIMQualifierDeclaration object #2 to be tested.
    #   * exp_equal: Expected equality of the object hash values.
    # * 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

    # Name tests
    (
        "Name, equal with same lexical case",
        dict(
            obj1=CIMQualifierDeclaration('Qual1', type='string'),
            obj2=CIMQualifierDeclaration('Qual1', type='string'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Name, equal with different lexical case",
        dict(
            obj1=CIMQualifierDeclaration('Qual1', type='string'),
            obj2=CIMQualifierDeclaration('quAL1', type='string'),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Name, different",
        dict(
            obj1=CIMQualifierDeclaration('Qual1', type='string'),
            obj2=CIMQualifierDeclaration('Qual1_x', type='string'),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Type tests
    (
        "Type, different",
        dict(
            obj1=CIMQualifierDeclaration('Qual1', type='uint8'),
            obj2=CIMQualifierDeclaration('Qual1', type='sint8'),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Value tests
    (
        "Value, strings with different lexical case",
        dict(
            obj1=CIMQualifierDeclaration('Qual1', type='string', value='abc'),
            obj2=CIMQualifierDeclaration('Qual1', type='string', value='Abc'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Value, strings with None / string",
        dict(
            obj1=CIMQualifierDeclaration('Qual1', type='string', value=None),
            obj2=CIMQualifierDeclaration('Qual1', type='string', value='abc'),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Value, strings with string / None",
        dict(
            obj1=CIMQualifierDeclaration('Qual1', type='string', value='abc'),
            obj2=CIMQualifierDeclaration('Qual1', type='string', value=None),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Value, strings with None / None",
        dict(
            obj1=CIMQualifierDeclaration('Qual1', type='string', value=None),
            obj2=CIMQualifierDeclaration('Qual1', type='string', value=None),
            exp_equal=True,
        ),
        None, None, True
    ),


    # Is_array tests
    (
        "Is_array, equal",
        dict(
            obj1=CIMQualifierDeclaration('Qual1', type='string',
                                         is_array=True),
            obj2=CIMQualifierDeclaration('Qual1', type='string',
                                         is_array=True),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Is_array, different",
        dict(
            obj1=CIMQualifierDeclaration('Qual1', type='string',
                                         is_array=True),
            obj2=CIMQualifierDeclaration('Qual1', type='string',
                                         is_array=False),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Array_size tests
    (
        "Array_size, equal",
        dict(
            obj1=CIMQualifierDeclaration('Qual1', type='string', is_array=True,
                                         array_size=2),
            obj2=CIMQualifierDeclaration('Qual1', type='string', is_array=True,
                                         array_size=2),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Array_size, different",
        dict(
            obj1=CIMQualifierDeclaration('Qual1', type='string', is_array=True,
                                         array_size=2),
            obj2=CIMQualifierDeclaration('Qual1', type='string', is_array=True,
                                         array_size=3),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Scopes tests
    (
        "Matching scopes, names with same lexical case",
        dict(
            obj1=CIMQualifierDeclaration(
                'Qual1', type='string', scopes=[
                    ('CLASS', True),
                ]
            ),
            obj2=CIMQualifierDeclaration(
                'Qual1', type='string', scopes=[
                    ('CLASS', True),
                ]
            ),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Matching scopes, names with different lexical case",
        dict(
            obj1=CIMQualifierDeclaration(
                'Qual1', type='string', scopes=[
                    ('CLASS', True),
                ]
            ),
            obj2=CIMQualifierDeclaration(
                'Qual1', type='string', scopes=[
                    ('claSS', True),
                ]
            ),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Non-matching scopes, one scope more",
        dict(
            obj1=CIMQualifierDeclaration(
                'Qual1', type='string', scopes=[
                    ('CLASS', True),
                ]
            ),
            obj2=CIMQualifierDeclaration(
                'Qual1', type='string', scopes=[
                    ('CLASS', True),
                    ('ASSOCIAION', True),
                ]
            ),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching scopes, one scope less",
        dict(
            obj1=CIMQualifierDeclaration(
                'Qual1', type='string', scopes=[
                    ('CLASS', True),
                    ('ASSOCIAION', True),
                ]
            ),
            obj2=CIMQualifierDeclaration(
                'Qual1', type='string', scopes=[
                    ('CLASS', True),
                ]
            ),
            exp_equal=False,
        ),
        None, None, True
    ),
    (
        "Non-matching scopes, different scopes",
        dict(
            obj1=CIMQualifierDeclaration(
                'Qual1', type='string', scopes=[
                    ('ASSOCIAION', True),
                ]
            ),
            obj2=CIMQualifierDeclaration(
                'Qual1', type='string', scopes=[
                    ('CLASS', True),
                ]
            ),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Overridable tests
    (
        "Overridable, equal",
        dict(
            obj1=CIMQualifierDeclaration('Qual1', value=None, type='string',
                                         overridable=True),
            obj2=CIMQualifierDeclaration('Qual1', value=None, type='string',
                                         overridable=True),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Overridable, different",
        dict(
            obj1=CIMQualifierDeclaration('Qual1', value=None, type='string',
                                         overridable=True),
            obj2=CIMQualifierDeclaration('Qual1', value=None, type='string',
                                         overridable=False),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Tosubclass tests
    (
        "Tosubclass, equal",
        dict(
            obj1=CIMQualifierDeclaration('Qual1', value=None, type='string',
                                         tosubclass=True),
            obj2=CIMQualifierDeclaration('Qual1', value=None, type='string',
                                         tosubclass=True),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Tosubclass, different",
        dict(
            obj1=CIMQualifierDeclaration('Qual1', value=None, type='string',
                                         tosubclass=True),
            obj2=CIMQualifierDeclaration('Qual1', value=None, type='string',
                                         tosubclass=False),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Toinstance tests
    (
        "Toinstance, equal",
        dict(
            obj1=CIMQualifierDeclaration('Qual1', value=None, type='string',
                                         toinstance=True),
            obj2=CIMQualifierDeclaration('Qual1', value=None, type='string',
                                         toinstance=True),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Toinstance, different",
        dict(
            obj1=CIMQualifierDeclaration('Qual1', value=None, type='string',
                                         toinstance=True),
            obj2=CIMQualifierDeclaration('Qual1', value=None, type='string',
                                         toinstance=False),
            exp_equal=False,
        ),
        None, None, True
    ),

    # Translatable tests
    (
        "Translatable, equal",
        dict(
            obj1=CIMQualifierDeclaration('Qual1', value=None, type='string',
                                         translatable=True),
            obj2=CIMQualifierDeclaration('Qual1', value=None, type='string',
                                         translatable=True),
            exp_equal=True,
        ),
        None, None, True
    ),
    (
        "Translatable, different",
        dict(
            obj1=CIMQualifierDeclaration('Qual1', value=None, type='string',
                                         translatable=True),
            obj2=CIMQualifierDeclaration('Qual1', value=None, type='string',
                                         translatable=False),
            exp_equal=False,
        ),
        None, None, True
    ),
]

TESTCASES_CIMQUALIFIERDECLARATION_EQ = [

    # Additional testcases for CIMQualifierDeclaration.__eq__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: CIMQualifierDeclaration object #1 to be tested.
    #   * obj2: CIMQualifierDeclaration object #2 to be tested.
    #   * exp_equal: Expected equality of the object hash values.
    # * 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

    # Exception testcases
    (
        "Incorrect second type: string",
        dict(
            obj1=CIMQualifierDeclaration('FooQualDecl', 'string'),
            obj2='FooQualDecl',
            exp_equal=None,
        ),
        TypeError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMQUALIFIERDECLARATION_HASH_EQ)
@simplified_test_function
@log_entry_exit
def test_CIMQualifierDeclaration_hash(testcase, obj1, obj2, exp_equal):
    """
    Test function for CIMQualifierDeclaration.__hash__().
    """

    # The code to be tested
    hash1 = hash(obj1)
    hash2 = hash(obj2)

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

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

    if exp_equal:
        assert hash1 == hash2
    else:
        assert hash1 != hash2


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMQUALIFIERDECLARATION_HASH_EQ +
    TESTCASES_CIMQUALIFIERDECLARATION_EQ)
@simplified_test_function
@log_entry_exit
def test_CIMQualifierDeclaration_eq(testcase, obj1, obj2, exp_equal):
    """
    Test function for CIMQualifierDeclaration.__eq__().
    """

    # The code to be tested
    equal = (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

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

    assert equal == exp_equal


TESTCASES_CIMQUALIFIERDECLARATION_STR_REPR = [

    # Testcases for CIMQualifierDeclaration.__repr__(),__str__() / repr(),str()

    # Each list item is a testcase tuple with these items:
    # * obj: CIMQualifierDeclaration object to be tested.

    (
        CIMQualifierDeclaration(
            name='FooQualDecl',
            type='string')
    ),
    (
        CIMQualifierDeclaration(
            name='FooQualDecl',
            type='string',
            value=['abc'],
            is_array=True,
            array_size=1,
            scopes=[('CLASS', True)],
            overridable=True,
            tosubclass=True,
            toinstance=False,
            translatable=False)
    ),
]


@pytest.mark.parametrize(
    "obj",
    TESTCASES_CIMQUALIFIERDECLARATION_STR_REPR)
@log_entry_exit
def test_CIMQualifierDeclaration_str(obj):
    """
    Test function for CIMQualifierDeclaration.__str__() / str()
    """

    # The code to be tested
    s = str(obj)

    assert re.match(r'^CIMQualifierDeclaration\(', s)

    exp_name = _format('name={0!A}', obj.name)
    assert exp_name in s

    exp_value = _format('value={0!A}', obj.value)
    assert exp_value in s

    exp_type = _format('type={0!A}', obj.type)
    assert exp_type in s

    exp_is_array = _format('is_array={0!A}', obj.is_array)
    assert exp_is_array in s


@pytest.mark.parametrize(
    "obj",
    TESTCASES_CIMQUALIFIERDECLARATION_STR_REPR)
@log_entry_exit
def test_CIMQualifierDeclaration_repr(obj):
    """
    Test function for CIMQualifierDeclaration.__repr__() / repr()
    """

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

    assert re.match(r'^CIMQualifierDeclaration\(', r)

    exp_name = _format('name={0!A}', obj.name)
    assert exp_name in r

    exp_value = _format('value={0!A}', obj.value)
    assert exp_value in r

    exp_type = _format('type={0!A}', obj.type)
    assert exp_type in r

    exp_is_array = _format('is_array={0!A}', obj.is_array)
    assert exp_is_array in r

    exp_array_size = _format('array_size={0!A}', obj.array_size)
    assert exp_array_size in r

    exp_scopes = _format('scopes={0!A}', obj.scopes)
    assert exp_scopes in r

    exp_tosubclass = _format('tosubclass={0!A}', obj.tosubclass)
    assert exp_tosubclass in r

    exp_overridable = _format('overridable={0!A}', obj.overridable)
    assert exp_overridable in r

    exp_translatable = _format('translatable={0!A}', obj.translatable)
    assert exp_translatable in r

    exp_toinstance = _format('toinstance={0!A}', obj.toinstance)
    assert exp_toinstance in r


TESTCASES_CIMQUALIFIERDECLARATION_TOCIMXML = [

    # Testcases for CIMQualifierDeclaration.tocimxml() and tocimxmlstr()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: CIMQualifierDeclaration object to be tested.
    #   * kwargs: Dict of input args for tocimxml() (empty).
    #   * exp_xml_str: Expected CIM-XML string, as a tuple/list of parts.
    # * 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

    # Tests with name variations
    (
        "Name with ASCII characters, as byte string",
        dict(
            obj=CIMQualifierDeclaration(b'Foo', value=None, type='string'),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Name with ASCII characters, as unicode string",
        dict(
            obj=CIMQualifierDeclaration('Foo', value=None, type='string'),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Name with non-ASCII UCS-2 characters, as byte string",
        dict(
            obj=CIMQualifierDeclaration(
                b'Foo\xC3\xA9', value=None, type='string'
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo\u00E9"',
                ' TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Name with non-ASCII UCS-2 characters, as unicode string",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo\u00E9', value=None, type='string'
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo\u00E9"',
                ' TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Name with non-UCS-2 characters, as byte string",
        dict(
            obj=CIMQualifierDeclaration(
                b'Foo\xF0\x90\x85\x82', value=None, type='string'
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo\U00010142"',
                ' TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Name with non-UCS-2 characters, as unicode string",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo\U00010142', value=None, type='string'
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo\U00010142"',
                ' TYPE="string"/>',
            )
        ),
        None, None, True
    ),

    # Variations of scopes argument
    (
        "QualifierDeclaration with scopes empty",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='boolean', value=None,
                scopes=[],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),
    (
        "QualifierDeclaration with scopes Association",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='boolean', value=None,
                scopes=[('ASSOCIATION', True)],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="boolean">',
                '<SCOPE ASSOCIATION="true"/>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "QualifierDeclaration with scopes Association, Property",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='boolean', value=None,
                scopes=[('ASSOCIATION', True), ('PROPERTY', True)],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="boolean">',
                '<SCOPE ASSOCIATION="true" PROPERTY="true"/>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "QualifierDeclaration with scopes Any",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='boolean', value=None,
                scopes=[('ANY', True)],
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="boolean">',
                '<SCOPE ASSOCIATION="true" CLASS="true" INDICATION="true"',
                ' METHOD="true" PARAMETER="true" PROPERTY="true" ',
                'REFERENCE="true"/>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Variations of overridable argument
    (
        "QualifierDeclaration with overridable True",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='boolean', value=None,
                overridable=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' OVERRIDABLE="true" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),
    (
        "QualifierDeclaration with overridable False",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='boolean', value=None,
                overridable=False,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' OVERRIDABLE="false" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),

    # Variations of tosubclass argument
    (
        "QualifierDeclaration with tosubclass True",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='boolean', value=None,
                tosubclass=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TOSUBCLASS="true" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),
    (
        "QualifierDeclaration with tosubclass False",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='boolean', value=None,
                tosubclass=False,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TOSUBCLASS="false" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),

    # Variations of toinstance argument
    (
        "QualifierDeclaration with toinstance True",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='boolean', value=None,
                toinstance=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TOINSTANCE="true" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),
    (
        "QualifierDeclaration with toinstance False",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='boolean', value=None,
                toinstance=False,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TOINSTANCE="false" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),

    # Variations of translatable argument
    (
        "QualifierDeclaration with translatable True",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='boolean', value=None,
                translatable=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TRANSLATABLE="true" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),
    (
        "QualifierDeclaration with translatable False",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='boolean', value=None,
                translatable=False,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TRANSLATABLE="false" TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifier declarations with boolean type
    (
        "Scalar qualifier declaration with boolean type, value NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='boolean', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with boolean type, value True",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='boolean', value=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="boolean">',
                '<VALUE>TRUE</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with boolean type, value False",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='boolean', value=False,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="boolean">',
                '<VALUE>FALSE</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifier declarations with string type
    (
        "Scalar qualifier declaration with string type, value NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='string', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with string type, value has one entry "
        "with ASCII characters",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='string', value='foo',
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="string">',
                '<VALUE>foo</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with string type, value has one entry "
        "with non-ASCII UCS-2 characters",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='string', value='foo\u00E9',
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="string">',
                '<VALUE>foo\u00E9</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with string type, value has one entry "
        "with non-UCS-2 characters",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='string', value='foo\U00010142',
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="string">',
                '<VALUE>foo\U00010142</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifier declarations with char16 type
    (
        "Scalar qualifier declaration with char16 type, value NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='char16', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="char16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with char16 type, value has one entry "
        "with an ASCII character",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='char16', value='f',
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="char16">',
                '<VALUE>f</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with char16 type, value has one entry "
        "with a non-ASCII UCS-2 character",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='char16', value='\u00E9',
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="char16">',
                '<VALUE>\u00E9</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with char16 type, value has one entry "
        "with non-UCS-2 character "
        "(invalid as per DSP0004, but tolerated by pywbem)",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='char16', value='\U00010142',
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="char16">',
                '<VALUE>\U00010142</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifier declarations with uint8 type
    (
        "Scalar qualifier declaration with uint8 type, value NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint8', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="uint8"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with uint8 type, value has one entry in "
        "range",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint8', value=42,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="uint8">',
                '<VALUE>42</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifier declarations with uint16 type
    (
        "Scalar qualifier declaration with uint16 type, value NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint16', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="uint16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with uint16 type, value has one entry "
        "in range",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint16', value=1234,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="uint16">',
                '<VALUE>1234</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifier declarations with uint32 type
    (
        "Scalar qualifier declaration with uint32 type, value NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint32', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="uint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with uint32 type, value has one entry "
        "in range",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint32', value=12345678,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="uint32">',
                '<VALUE>12345678</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifier declarations with uint64 type
    (
        "Scalar qualifier declaration with uint64 type, value NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint64', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="uint64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with uint64 type, value has one entry "
        "in range",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint64', value=123456789012,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="uint64">',
                '<VALUE>123456789012</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifier declarations with sint8 type
    (
        "Scalar qualifier declaration with sint8 type, value NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint8', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="sint8"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with sint8 type, value has one entry "
        "in range",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint8', value=-42,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="sint8">',
                '<VALUE>-42</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifier declarations with sint16 type
    (
        "Scalar qualifier declaration with sint16 type, value NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint16', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="sint16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with sint16 type, value has one entry "
        "in range",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint16', value=-1234,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="sint16">',
                '<VALUE>-1234</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifier declarations with sint32 type
    (
        "Scalar qualifier declaration with sint32 type, value NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint32', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="sint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with sint32 type, value has one entry "
        "in range",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint32', value=-12345678,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="sint32">',
                '<VALUE>-12345678</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifier declarations with sint64 type
    (
        "Scalar qualifier declaration with sint64 type, value NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint64', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="sint64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with sint64 type, value has one entry "
        "in range",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint64', value=-123456789012,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="sint64">',
                '<VALUE>-123456789012</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifier declarations with real32 type
    (
        "Scalar qualifier declaration with real32 type, value NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="real32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with real32 type, value between 0 and 1",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=0.42,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="real32">',
                '<VALUE>0.42</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with real32 type, value with max number "
        "of significant digits (11)",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=1.2345678901,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="real32">',
                '<VALUE>1.2345678901</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with real32 type, value larger 1 "
        "without exponent",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=42.0,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="real32">',
                '<VALUE>42.0</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with real32 type, value with small "
        "negative exponent",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=-42.0E-3,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="real32">',
                '<VALUE>-0.042</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with real32 type, value with small "
        "positive exponent",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=-42.0E+3,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="real32">',
                '<VALUE>-42000.0</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with real32 type, value with large "
        "negative exponent",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=-42.0E-30,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="real32">',
                '<VALUE>-4.2E-29</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with real32 type, value with large "
        "positive exponent",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=-42.0E+30,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="real32">',
                '<VALUE>-4.2E+31</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with real32 type, special value INF",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=float('inf'),
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="real32">',
                '<VALUE>INF</VALUE>',  # must be upper case
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with real32 type, special value -INF",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=float('-inf'),
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="real32">',
                '<VALUE>-INF</VALUE>',  # must be upper case
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with real32 type, special value NaN",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=float('nan'),
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="real32">',
                '<VALUE>NaN</VALUE>',  # must be upper case
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifier declarations with real64 type
    (
        "Scalar qualifier declaration with real64 type, value NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="real64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with real64 type, value between 0 and 1",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=0.42,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="real64">',
                '<VALUE>0.42</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, False  # py27: 0.41999999999999998
    ),
    (
        "Scalar qualifier declaration with real64 type, value with max number "
        "of significant digits (17)",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=1.2345678901234567,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="real64">',
                '<VALUE>1.2345678901234567</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with real64 type, value larger 1 "
        "without exponent",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=42.0,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="real64">',
                '<VALUE>42.0</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with real64 type, value with small "
        "negative exponent",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=-42.0E-3,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="real64">',
                '<VALUE>-0.042</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, False  # py27: -0.042000000000000003
    ),
    (
        "Scalar qualifier declaration with real64 type, value with small "
        "positive exponent",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=-42.0E+3,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="real64">',
                '<VALUE>-42000.0</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with real64 type, value with large "
        "negative exponent",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=-42.0E-30,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="real64">',
                '<VALUE>-4.2E-29</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, False  # py27: -4.1999999999999998E-29
    ),
    (
        "Scalar qualifier declaration with real64 type, value with large "
        "positive exponent",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=-42.0E+30,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="real64">',
                '<VALUE>-4.2E+31</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, False  # py27: -4.1999999999999996E+31
    ),
    (
        "Scalar qualifier declaration with real64 type, special value INF",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=float('inf'),
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="real64">',
                '<VALUE>INF</VALUE>',  # must be upper case
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with real64 type, special value -INF",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=float('-inf'),
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="real64">',
                '<VALUE>-INF</VALUE>',  # must be upper case
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with real64 type, special value NaN",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=float('nan'),
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="real64">',
                '<VALUE>NaN</VALUE>',  # must be upper case
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Scalar qualifier declarations with datetime type
    (
        "Scalar qualifier declaration with datetime type, value NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='datetime', value=None,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="datetime"/>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with datetime type, point in time value",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='datetime',
                value=datetime(2014, 9, 22, 10, 49, 20, 524789),
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="datetime">',
                '<VALUE>20140922104920.524789+000</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Scalar qualifier declaration with datetime type, interval value",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='datetime',
                value=timedelta(10, 49, 20),
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="false" NAME="Foo"',
                ' TYPE="datetime">',
                '<VALUE>00000010000049.000020:000</VALUE>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # QualifierDeclarations with reference type are not allowed as per DSP0004

    # Array qualifier declarations with boolean type
    (
        "Array qualifier declaration with boolean type, value is NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='boolean', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="boolean"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with boolean type, value is empty array",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='boolean', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="boolean">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with boolean type, value has one entry "
        "NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='boolean', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="boolean">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with boolean type, value has one entry "
        "True",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='boolean', value=[True],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="boolean">',
                '<VALUE.ARRAY>',
                '<VALUE>TRUE</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with boolean type, value has one entry "
        "False",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='boolean', value=[False],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="boolean">',
                '<VALUE.ARRAY>',
                '<VALUE>FALSE</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Array qualifier declarations with string type
    (
        "Array qualifier declaration with string type, value is NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='string', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="string"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with string type, value is empty array",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='string', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="string">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with string type, value has one entry "
        "NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='string', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="string">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with string type, value has one entry "
        "with ASCII characters",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='string', value=['foo'],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="string">',
                '<VALUE.ARRAY>',
                '<VALUE>foo</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with string type, value has one entry "
        "with non-ASCII UCS-2 characters",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='string', value=['foo\u00E9'],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="string">',
                '<VALUE.ARRAY>',
                '<VALUE>foo\u00E9</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with string type, value has one entry "
        "with non-UCS-2 characters",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='string', value=['foo\U00010142'],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="string">',
                '<VALUE.ARRAY>',
                '<VALUE>foo\U00010142</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Array qualifier declarations with char16 type
    (
        "Array qualifier declaration with char16 type, value is NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='char16', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="char16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with char16 type, value is empty array",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='char16', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="char16">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with char16 type, value has one entry "
        "NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='char16', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="char16">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with char16 type, value has one entry "
        "with an ASCII character",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='char16', value=['f'],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="char16">',
                '<VALUE.ARRAY>',
                '<VALUE>f</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with char16 type, value has one entry "
        "with a non-ASCII UCS-2 character",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='char16', value=['\u00E9'],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="char16">',
                '<VALUE.ARRAY>',
                '<VALUE>\u00E9</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with char16 type, value has one entry "
        "with a non-UCS-2 character "
        "(invalid as per DSP0004, but tolerated by pywbem)",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='char16', value=['\U00010142'],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="char16">',
                '<VALUE.ARRAY>',
                '<VALUE>\U00010142</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Array qualifier declarations with uint8 type
    (
        "Array qualifier declaration with uint8 type, value is NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint8', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="uint8"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with uint8 type, value is empty array",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint8', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="uint8">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with uint8 type, value has one entry NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint8', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="uint8">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with uint8 type, value has one entry in "
        "range",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint8', value=[42],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="uint8">',
                '<VALUE.ARRAY>',
                '<VALUE>42</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Array qualifier declarations with uint16 type
    (
        "Array qualifier declaration with uint16 type, value is NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint16', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="uint16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with uint16 type, value is empty array",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint16', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="uint16">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with uint16 type, value has one entry "
        "NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint16', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="uint16">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with uint16 type, value has one entry "
        "in range",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint16', value=[1234],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="uint16">',
                '<VALUE.ARRAY>',
                '<VALUE>1234</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Array qualifier declarations with uint32 type
    (
        "Array qualifier declaration with uint32 type, value is NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint32', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="uint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with uint32 type, value is empty array",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint32', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="uint32">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with uint32 type, value has one entry "
        "NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint32', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="uint32">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with uint32 type, value has one entry "
        "in range",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint32', value=[12345678],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="uint32">',
                '<VALUE.ARRAY>',
                '<VALUE>12345678</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Array qualifier declarations with uint64 type
    (
        "Array qualifier declaration with uint64 type, value is NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint64', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="uint64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with uint64 type, value is empty array",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint64', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="uint64">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with uint64 type, value has one entry "
        "NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint64', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="uint64">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with uint64 type, value has one entry "
        "in range",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='uint64', value=[123456789012],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="uint64">',
                '<VALUE.ARRAY>',
                '<VALUE>123456789012</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Array qualifier declarations with sint8 type
    (
        "Array qualifier declaration with sint8 type, value is NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint8', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="sint8"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with sint8 type, value is empty array",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint8', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="sint8">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with sint8 type, value has one entry NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint8', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="sint8">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with sint8 type, value has one entry "
        "in range",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint8', value=[-42],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="sint8">',
                '<VALUE.ARRAY>',
                '<VALUE>-42</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Array qualifier declarations with sint16 type
    (
        "Array qualifier declaration with sint16 type, value is NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint16', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="sint16"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with sint16 type, value is empty array",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint16', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="sint16">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with sint16 type, value has one entry "
        "NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint16', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="sint16">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with sint16 type, value has one entry "
        "in range",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint16', value=[-1234],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="sint16">',
                '<VALUE.ARRAY>',
                '<VALUE>-1234</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Array qualifier declarations with sint32 type
    (
        "Array qualifier declaration with sint32 type, value is NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint32', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="sint32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with sint32 type, value is empty array",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint32', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="sint32">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with sint32 type, value has one entry "
        "NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint32', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="sint32">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with sint32 type, value has one entry "
        "in range",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint32', value=[-12345678],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="sint32">',
                '<VALUE.ARRAY>',
                '<VALUE>-12345678</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Array qualifier declarations with sint64 type
    (
        "Array qualifier declaration with sint64 type, value is NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint64', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="sint64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with sint64 type, value is empty array",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint64', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="sint64">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with sint64 type, value has one entry "
        "NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint64', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="sint64">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with sint64 type, value has one entry "
        "in range",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='sint64', value=[-123456789012],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="sint64">',
                '<VALUE.ARRAY>',
                '<VALUE>-123456789012</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Array qualifier declarations with real32 type
    (
        "Array qualifier declaration with real32 type, value is NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real32"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with real32 type, value is empty array",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real32">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with real32 type, value has one entry "
        "NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with real32 type, value has one entry "
        "between 0 and 1",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=[0.42],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>0.42</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with real32 type, value has one entry "
        "with max number of significant digits (11)",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=[1.2345678901],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>1.2345678901</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with real32 type, value has one entry "
        "larger 1 without exponent",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=[42.0],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>42.0</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with real32 type, value has one entry "
        "with small negative exponent",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=[-42.0E-3],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>-0.042</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with real32 type, value has one entry "
        "with small positive exponent",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=[-42.0E+3],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>-42000.0</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with real32 type, value has one entry "
        "with large negative exponent",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=[-42.0E-30],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>-4.2E-29</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with real32 type, value has one entry "
        "with large positive exponent",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=[-42.0E+30],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>-4.2E+31</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with real32 type, value has one entry "
        "that is special value INF",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=[float('inf')],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>INF</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with real32 type, value has one entry "
        "that is special value -INF",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=[float('-inf')],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>-INF</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with real32 type, value has one entry "
        "that is special value NaN",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real32', value=[float('nan')],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real32">',
                '<VALUE.ARRAY>',
                '<VALUE>NaN</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Array qualifier declarations with real64 type
    (
        "Array qualifier declaration with real64 type, value is NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real64"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with real64 type, value is empty array",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real64">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with real64 type, value has one entry "
        "NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with real64 type, value has one entry "
        "between 0 and 1",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=[0.42],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>0.42</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, False  # py27: 0.41999999999999998
    ),
    (
        "Array qualifier declaration with real64 type, value has one entry "
        "with max number of significant digits (17)",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=[1.2345678901234567],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>1.2345678901234567</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with real64 type, value has one entry "
        "larger 1 without exponent",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=[42.0],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>42.0</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with real64 type, value has one entry "
        "with small negative exponent",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=[-42.0E-3],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>-0.042</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, False  # py27: -0.042000000000000003
    ),
    (
        "Array qualifier declaration with real64 type, value has one entry "
        "with small positive exponent",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=[-42.0E+3],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>-42000.0</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with real64 type, value has one entry "
        "with large negative exponent",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=[-42.0E-30],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>-4.2E-29</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, False  # py27: -4.1999999999999998E-29
    ),
    (
        "Array qualifier declaration with real64 type, value has one entry "
        "with large positive exponent",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=[-42.0E+30],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>-4.2E+31</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, False  # py27: -4.1999999999999996E+31
    ),
    (
        "Array qualifier declaration with real64 type, value has one entry "
        "that is special value INF",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=[float('inf')],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>INF</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with real64 type, value has one entry "
        "that is special value -INF",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=[float('-inf')],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>-INF</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with real64 type, value has one entry "
        "that is special value NaN",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='real64', value=[float('nan')],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="real64">',
                '<VALUE.ARRAY>',
                '<VALUE>NaN</VALUE>',  # must be upper case
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),

    # Array qualifier declarations with datetime type
    (
        "Array qualifier declaration with datetime type, value is NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='datetime', value=None,
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="datetime"/>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with datetime type, value is empty array",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='datetime', value=[],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="datetime">',
                '<VALUE.ARRAY/>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with datetime type, value has one entry "
        "NULL",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='datetime', value=[None],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="datetime">',
                '<VALUE.ARRAY>',
                '<VALUE.NULL/>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with datetime type, value has one entry "
        "point in time",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='datetime',
                value=[datetime(2014, 9, 22, 10, 49, 20, 524789)],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="datetime">',
                '<VALUE.ARRAY>',
                '<VALUE>20140922104920.524789+000</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
    (
        "Array qualifier declaration with datetime type, value has one entry "
        "interval",
        dict(
            obj=CIMQualifierDeclaration(
                'Foo', type='datetime',
                value=[timedelta(10, 49, 20)],
                is_array=True,
            ),
            kwargs={},
            exp_xml_str=(
                '<QUALIFIER.DECLARATION ISARRAY="true" NAME="Foo"',
                ' TYPE="datetime">',
                '<VALUE.ARRAY>',
                '<VALUE>00000010000049.000020:000</VALUE>',
                '</VALUE.ARRAY>',
                '</QUALIFIER.DECLARATION>',
            )
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMQUALIFIERDECLARATION_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMQualifierDeclaration_tocimxml(testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMQualifierDeclaration.tocimxml().
    """

    # The code to be tested
    obj_xml = obj.tocimxml(**kwargs)

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

    obj_xml_str = obj_xml.toxml()
    exp_xml_str = ''.join(exp_xml_str)
    validate_cim_xml_obj(obj, obj_xml_str, exp_xml_str)


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMQUALIFIERDECLARATION_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMQualifierDeclaration_tocimxmlstr(
        testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMQualifierDeclaration.tocimxmlstr().
    """

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(**kwargs)

    # 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(obj_xml_str, str)

    exp_xml_str = ''.join(exp_xml_str)
    validate_cim_xml_obj(obj, obj_xml_str, exp_xml_str)


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMQUALIFIERDECLARATION_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMQualifierDeclaration_tocimxmlstr_indent_int(
        testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMQualifierDeclaration.tocimxmlstr() with indent as
    integer.
    """

    indent = 4
    indent_str = ' ' * indent

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(indent=indent, **kwargs)

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

    obj_xml = obj.tocimxml(**kwargs)  # This is tested elsewhere
    exp_xml_str = obj_xml.toprettyxml(indent=indent_str)

    assert obj_xml_str == exp_xml_str, (
        f"{obj.__class__.__name__}.tocimxmlstr(indent={indent!r}) "
        "returns unexpected CIM-XML string")


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMQUALIFIERDECLARATION_TOCIMXML)
@simplified_test_function
@log_entry_exit
def test_CIMQualifierDeclaration_tocimxmlstr_indent_str(
        testcase, obj, kwargs, exp_xml_str):
    """
    Test function for CIMQualifierDeclaration.tocimxmlstr() with indent as
    string.
    """

    indent = 4
    indent_str = ' ' * indent

    # The code to be tested
    obj_xml_str = obj.tocimxmlstr(indent=indent_str, **kwargs)

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

    obj_xml = obj.tocimxml(**kwargs)  # This is tested elsewhere
    exp_xml_str = obj_xml.toprettyxml(indent=indent_str)

    assert obj_xml_str == exp_xml_str, (
        f"{obj.__class__.__name__}.tocimxmlstr(indent={indent_str!r}) "
        "returns unexpected CIM-XML string")


TESTCASES_CIMQUALIFIERDECLARATION_TOMOF = [

    # Testcases for CIMQualifierDeclaration.tomof()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: CIMQualifierDeclaration object to be tested.
    #   * kwargs: Dict of input args to tomof() method
    #   * exp_mof: Expected MOF result string, or None if failed.
    # * 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

    (
        "all components",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='string',
                is_array=True,
                array_size=5,
                value=["abc"],
                scopes=[('PROPERTY', True), ('METHOD', True),
                        ('PARAMETER', True)],
                overridable=True,
                tosubclass=True,
                toinstance=True,
                translatable=True,
            ),
            kwargs={},
            exp_mof="""\
Qualifier Q1 : string[5] = { "abc" },
    Scope(property, method, parameter),
    Flavor(EnableOverride, ToSubclass, Translatable);
""",
        ),
        None, None, True
    ),
    (
        "string type, no default value",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='string',
                value=None,
            ),
            kwargs={},
            exp_mof="""\
Qualifier Q1 : string,
    Scope();
""",
        ),
        None, None, True
    ),
    (
        "string type, default value with escape sequences dq,sq,bs",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='string',
                value="dq=\",sq=\',bs=\\",
            ),
            kwargs={},
            exp_mof="""\
Qualifier Q1 : string = "dq=\\",sq=\\',bs=\\\\",
    Scope();
""",
        ),
        None, None, True
    ),
    (
        "string type, default value with escape sequences bt,tb,nl,vt,cr",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='string',
                value="bt=\b,tb=\t,nl=\n,vt=\f,cr=\r",
            ),
            kwargs={},
            exp_mof="""\
Qualifier Q1 : string = "bt=\\b,tb=\\t,nl=\\n,vt=\\f,cr=\\r",
    Scope();
""",
        ),
        None, None, True
    ),
    (
        "char16 type, default value nl",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='char16',
                value="\n",
            ),
            kwargs={},
            exp_mof="""\
Qualifier Q1 : char16 = '\\n',
    Scope();
""",
        ),
        None, None, False  # TODO 01/18 AM Enable once char16 uses single quotes
    ),
    (
        "boolean type, default value",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='boolean',
                value=False,
            ),
            kwargs={},
            exp_mof="""\
Qualifier Q1 : boolean = false,
    Scope();
""",
        ),
        None, None, True
    ),
    (
        "uint32 type, default value",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='uint32',
                value=42,
            ),
            kwargs={},
            exp_mof="""\
Qualifier Q1 : uint32 = 42,
    Scope();
""",
        ),
        None, None, True
    ),
    (
        "real32 type, default value",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='real32',
                value=Real32(42.1),
            ),
            kwargs={},
            exp_mof="""\
Qualifier Q1 : real32 = 42.1,
    Scope();
""",
        ),
        # unpredictable value before 0.12
        None, None, True
    ),
    (
        "datetime type, default value",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='datetime',
                value=CIMDateTime('20140924193040.654321+120'),
            ),
            kwargs={},
            exp_mof="""\
Qualifier Q1 : datetime = "20140924193040.654321+120",
    Scope();
""",
        ),
        None, None, True
    ),
    (
        "string array of variable size that has an empty array value",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='string',
                is_array=True,
                array_size=None,
                value=[],
            ),
            kwargs={},
            exp_mof="""\
Qualifier Q1 : string[] = {  },
    Scope();
""",
        ),
        None, None, True
    ),
    (
        "string array of variable size",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='string',
                is_array=True,
                array_size=None,
                value=["abc", "def"],
            ),
            kwargs={},
            exp_mof="""\
Qualifier Q1 : string[] = { "abc", "def" },
    Scope();
""",
        ),
        None, None, True
    ),
    (
        "string array of fixed size",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='string',
                is_array=True,
                array_size=5,
                value=["abc", "def"],
            ),
            kwargs={},
            exp_mof="""\
Qualifier Q1 : string[5] = { "abc", "def" },
    Scope();
""",
        ),
        None, None, True
    ),
    (
        "single scope",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='string',
                scopes=[('CLASS', True)],
            ),
            kwargs={},
            exp_mof="""\
Qualifier Q1 : string,
    Scope(class);
""",
        ),
        None, None, True
    ),
    (
        "all scopes as a list",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='string',
                scopes=[('CLASS', True), ('ASSOCIATION', True),
                        ('INDICATION', True), ('PROPERTY', True),
                        ('REFERENCE', True), ('METHOD', True),
                        ('PARAMETER', True)],
            ),
            kwargs={},
            # pylint: disable=line-too-long
            exp_mof="""\
Qualifier Q1 : string,
    Scope(class, association, indication, property, reference, method, parameter);
""",  # noqa: E501
        ),
        # unpredictable order of scopes before 0.12
        None, None, True
    ),
    (
        "all scopes as any",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='string',
                scopes=[('ANY', True)],
            ),
            kwargs={},
            exp_mof="""\
Qualifier Q1 : string,
    Scope(any);
""",
        ),
        None, None, True
    ),
    (
        "flavor EnableOverride",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='string',
                overridable=True,
            ),
            kwargs={},
            exp_mof="""\
Qualifier Q1 : string,
    Scope(),
    Flavor(EnableOverride);
""",
        ),
        None, None, True
    ),
    (
        "flavor ToSubclass EnableOverride",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='string',
                overridable=True,
                tosubclass=True,
            ),
            kwargs={},
            exp_mof="""\
Qualifier Q1 : string,
    Scope(),
    Flavor(EnableOverride, ToSubclass);
""",
        ),
        None, None, True
    ),
    (
        "flavor DisableOverride",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='string',
                overridable=False,
            ),
            kwargs={},
            exp_mof="""\
Qualifier Q1 : string,
    Scope(),
    Flavor(DisableOverride);
""",
        ),
        None, None, True
    ),
    (
        "flavor Restricted",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='string',
                tosubclass=False,
            ),
            kwargs={},
            exp_mof="""\
Qualifier Q1 : string,
    Scope(),
    Flavor(Restricted);
""",
        ),
        None, None, True
    ),
    (
        "flavor ToSubclass",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='string',
                tosubclass=True,
            ),
            kwargs={},
            exp_mof="""\
Qualifier Q1 : string,
    Scope(),
    Flavor(ToSubclass);
""",
        ),
        None, None, True
    ),
    (
        "flavor Translatable",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='string',
                translatable=True,
            ),
            kwargs={},
            exp_mof="""\
Qualifier Q1 : string,
    Scope(),
    Flavor(Translatable);
""",
        ),
        None, None, True
    ),
    (
        "flavor ToInstance (not generated because not in DSP0004)",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='string',
                toinstance=True,
            ),
            kwargs={},
            exp_mof="""\
Qualifier Q1 : string,
    Scope();
""",
        ),
        None, None, True
    ),
    (
        "Qualifier decl with name that does not fit onto line by 10",
        dict(
            obj=CIMQualifierDeclaration(
                name='Very_long_qualifier_name',
                type='string',
            ),
            kwargs=dict(
                maxline=10,
            ),
            exp_mof=''
            'Qualifier Very_long_qualifier_name : string,\n'
            '    Scope();\n',
        ),
        None, None, True
    ),
    (
        "Qualifier decl with uint64 value that does not fit onto line by 1",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='uint64',
                value=Uint64(1998012513304050)
            ),
            kwargs=dict(
                maxline=21,
            ),
            exp_mof=None,
        ),
        ValueError, None, True
    ),
    (
        "Qualifier decl with uint64 value that exactly fits onto line",
        dict(
            obj=CIMQualifierDeclaration(
                name='Q1',
                type='uint64',
                value=Uint64(1998012513304050)
            ),
            kwargs=dict(
                maxline=22,
            ),
            exp_mof=''
            'Qualifier Q1 : uint64 = \n'
            '   1998012513304050,\n'
            '    Scope();\n',
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMQUALIFIERDECLARATION_TOMOF)
@simplified_test_function
@log_entry_exit
def test_CIMQualifierDeclaration_tomof(testcase, obj, kwargs, exp_mof):
    """
    Test function for CIMQualifierDeclaration.tomof().
    """

    # The code to be tested
    mof = obj.tomof(**kwargs)

    # 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(mof, str)
    assert mof == exp_mof


TESTCASES_CIMVALUE = [

    # Testcases for cimvalue()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * args: List of positional arguments for cimvalue(), see kwargs.
    #   * kwargs: Dict of keyword arguments for cimvalue():
    #     - value: The input value.
    #     - type: The input type (as a CIM type name).
    #   * exp_obj: Expected CIM value object, or None if failed.
    # * 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

    # Order of positional arguments
    (
        "Verify order of positional arguments",
        dict(
            args=['a', 'string'],
            kwargs={},
            exp_obj='a',
        ),
        None, None, True,
    ),

    # Test cases where Type is None (and is inferred from the value)
    (
        "Type is None (inferred from value), value is None",
        dict(
            args=[],
            kwargs=dict(value=None, type=None),
            exp_obj=None,
        ),
        None, None, True
    ),
    (
        "Type is None (inferred from value), value is unicode string",
        dict(
            args=[],
            kwargs=dict(value='a', type=None),
            exp_obj='a',
        ),
        None, None, True
    ),
    (
        "Type is None (inferred from value), value is byte string",
        dict(
            args=[],
            kwargs=dict(value=b'a', type=None),
            exp_obj='a',
        ),
        None, None, True
    ),
    (
        "Type is None (inferred from value), value is string 'true'",
        dict(
            args=[],
            kwargs=dict(value='true', type=None),
            exp_obj='true',
        ),
        None, None, True
    ),
    (
        "Type is None (inferred from value), value is string 'false'",
        dict(
            args=[],
            kwargs=dict(value='false', type=None),
            exp_obj='false',
        ),
        None, None, True
    ),
    (
        "Type is None (inferred from value), value is datetime string",
        dict(
            args=[],
            kwargs=dict(value=DATETIME1_STR, type=None),
            exp_obj=DATETIME1_STR,
        ),
        None, None, True
    ),
    (
        "Type is None (inferred from value), value is timedelta string",
        dict(
            args=[],
            kwargs=dict(value=TIMEDELTA1_STR, type=None),
            exp_obj=TIMEDELTA1_STR,
        ),
        None, None, True
    ),
    (
        "Type is None (inferred from value), value is reference string",
        dict(
            args=[],
            kwargs=dict(value=REF1_STR, type=None),
            exp_obj=REF1_STR,
        ),
        None, None, True
    ),
    (
        "Type is None (inferred from value), value is True",
        dict(
            args=[],
            kwargs=dict(value=True, type=None),
            exp_obj=True,
        ),
        None, None, True
    ),
    (
        "Type is None (inferred from value), value is False",
        dict(
            args=[],
            kwargs=dict(value=False, type=None),
            exp_obj=False,
        ),
        None, None, True
    ),
    (
        "Type is None (inferred from value), value is CIMDateTime object",
        dict(
            args=[],
            kwargs=dict(value=DATETIME1_DT, type=None),
            exp_obj=DATETIME1_OBJ,
        ),
        None, None, True
    ),
    (
        "Type is None (inferred from value), value is CIMDateTime delta",
        dict(
            args=[],
            kwargs=dict(value=TIMEDELTA1_TD, type=None),
            exp_obj=TIMEDELTA1_OBJ,
        ),
        None, None, True
    ),

    # Test cases where type is string
    (
        "Type is string, value is None",
        dict(
            args=[],
            kwargs=dict(value=None, type='string'),
            exp_obj=None,
        ),
        None, None, True
    ),
    (
        "Type is string, value is empty string",
        dict(
            args=[],
            kwargs=dict(value='', type='string'),
            exp_obj='',
        ),
        None, None, True
    ),
    (
        "Type is string, value is one-char unicode string",
        dict(
            args=[],
            kwargs=dict(value='a', type='string'),
            exp_obj='a',
        ),
        None, None, True
    ),
    (
        "Type is string, value is short unicode string",
        dict(
            args=[],
            kwargs=dict(value='abc', type='string'),
            exp_obj='abc',
        ),
        None, None, True
    ),
    (
        "Type is string, value is non-ASCII unicode string U+00E4 "
        "(lower case a umlaut)",
        dict(
            args=[],
            kwargs=dict(value='\u00E4', type='string'),
            exp_obj='\u00E4',
        ),
        None, None, True
    ),
    (
        "Type is string, value is empty byte string",
        dict(
            args=[],
            kwargs=dict(value=b'', type='string'),
            exp_obj='',
        ),
        None, None, True
    ),
    (
        "Type is string, value is one-char byte string",
        dict(
            args=[],
            kwargs=dict(value=b'a', type='string'),
            exp_obj='a',
        ),
        None, None, True
    ),
    (
        "Type is string, value is short byte string",
        dict(
            args=[],
            kwargs=dict(value=b'abc', type='string'),
            exp_obj='abc',
        ),
        None, None, True
    ),

    # Test cases where type is char16
    (
        "Type is char16, value is None",
        dict(
            args=[],
            kwargs=dict(value=None, type='char16'),
            exp_obj=None,
        ),
        None, None, True
    ),
    (
        "Type is char16, value is empty Char16 string",
        dict(
            args=[],
            kwargs=dict(value=Char16(''), type='char16'),
            exp_obj='',
        ),
        None, None, True
    ),
    (
        "Type is char16, value is empty unicode string",
        dict(
            args=[],
            kwargs=dict(value='', type='char16'),
            exp_obj='',
        ),
        None, None, True
    ),
    (
        "Type is char16, value is one-char Char16 string",
        dict(
            args=[],
            kwargs=dict(value=Char16('a'), type='char16'),
            exp_obj='a',
        ),
        None, None, True
    ),
    (
        "Type is char16, value is one-char unicode string",
        dict(
            args=[],
            kwargs=dict(value='a', type='char16'),
            exp_obj='a',
        ),
        None, None, True
    ),
    (
        "Type is char16, value is non-ASCII Char16 string",
        dict(
            args=[],
            kwargs=dict(value=Char16('\u00E4'), type='char16'),
            exp_obj='\u00E4',
        ),
        None, None, True
    ),
    (
        "Type is char16, value is non-ASCII unicode string",
        dict(
            args=[],
            kwargs=dict(value='\u00E4', type='char16'),
            exp_obj='\u00E4',
        ),
        None, None, True
    ),

    (
        "Type is char16, value is empty byte string",
        dict(
            args=[],
            kwargs=dict(value=b'', type='char16'),
            exp_obj='',
        ),
        None, None, True
    ),
    (
        "Type is char16, value is one-char byte string",
        dict(
            args=[],
            kwargs=dict(value=b'a', type='char16'),
            exp_obj='a',
        ),
        None, None, True
    ),

    # Test cases where type is boolean
    (
        "Type is boolean, value is None",
        dict(
            args=[],
            kwargs=dict(value=None, type='boolean'),
            exp_obj=None,
        ),
        None, None, True
    ),
    (
        "Type is boolean, value is True",
        dict(
            args=[],
            kwargs=dict(value=True, type='boolean'),
            exp_obj=True,
        ),
        None, None, True
    ),
    (
        "Type is boolean, value is False",
        dict(
            args=[],
            kwargs=dict(value=False, type='boolean'),
            exp_obj=False,
        ),
        None, None, True
    ),
    (
        "Type is boolean, value is short string",
        dict(
            args=[],
            kwargs=dict(value='abc', type='boolean'),
            exp_obj=True,
        ),
        None, None, True
    ),
    (
        "Type is boolean, value is string 'true' (no special treatment)",
        dict(
            args=[],
            kwargs=dict(value='true', type='boolean'),
            exp_obj=True,
        ),
        None, None, True
    ),
    (
        "Type is boolean, value is string 'false' (no special treatment)",
        dict(
            args=[],
            kwargs=dict(value='false', type='boolean'),
            exp_obj=True,
        ),
        None, None, True
    ),
    (
        "Type is boolean, value is empty string",
        dict(
            args=[],
            kwargs=dict(value='', type='boolean'),
            exp_obj=False,
        ),
        None, None, True
    ),
    (
        "Type is boolean, value is integer 0",
        dict(
            args=[],
            kwargs=dict(value=0, type='boolean'),
            exp_obj=False,
        ),
        None, None, True
    ),
    (
        "Type is boolean, value is integer 1",
        dict(
            args=[],
            kwargs=dict(value=1, type='boolean'),
            exp_obj=True,
        ),
        None, None, True
    ),
    (
        "Type is boolean, value is integer -1",
        dict(
            args=[],
            kwargs=dict(value=-1, type='boolean'),
            exp_obj=True,
        ),
        None, None, True
    ),

    # Test cases where type is uint8
    (
        "Type is uint8, value is None",
        dict(
            args=[],
            kwargs=dict(value=None, type='uint8'),
            exp_obj=None,
        ),
        None, None, True
    ),
    (
        "Type is uint8, value is integer 0",
        dict(
            args=[],
            kwargs=dict(value=0, type='uint8'),
            exp_obj=Uint8(0),
        ),
        None, None, True
    ),
    (
        "Type is uint8, value is integer MAX_UINT8",
        dict(
            args=[],
            kwargs=dict(value=MAX_UINT8, type='uint8'),
            exp_obj=Uint8(MAX_UINT8),
        ),
        None, None, True
    ),
    (
        "Type is uint8, value is Uint8 0",
        dict(
            args=[],
            kwargs=dict(value=Uint8(0), type='uint8'),
            exp_obj=Uint8(0),
        ),
        None, None, True
    ),
    (
        "Type is uint8, value is Uint8 MAX_UINT8",
        dict(
            args=[],
            kwargs=dict(value=Uint8(MAX_UINT8), type='uint8'),
            exp_obj=Uint8(MAX_UINT8),
        ),
        None, None, True
    ),

    # Test cases where type is uint16
    (
        "Type is uint16, value is None",
        dict(
            args=[],
            kwargs=dict(value=None, type='uint16'),
            exp_obj=None,
        ),
        None, None, True
    ),
    (
        "Type is uint16, value is integer 0",
        dict(
            args=[],
            kwargs=dict(value=0, type='uint16'),
            exp_obj=Uint16(0),
        ),
        None, None, True
    ),
    (
        "Type is uint16, value is integer MAX_UINT16",
        dict(
            args=[],
            kwargs=dict(value=MAX_UINT16, type='uint16'),
            exp_obj=Uint16(MAX_UINT16),
        ),
        None, None, True
    ),
    (
        "Type is uint16, value is Uint16 0",
        dict(
            args=[],
            kwargs=dict(value=Uint16(0), type='uint16'),
            exp_obj=Uint16(0),
        ),
        None, None, True
    ),
    (
        "Type is uint16, value is Uint16 MAX_UINT16",
        dict(
            args=[],
            kwargs=dict(value=Uint16(MAX_UINT16), type='uint16'),
            exp_obj=Uint16(MAX_UINT16),
        ),
        None, None, True
    ),

    # Test cases where type is uint32
    (
        "Type is uint32, value is None",
        dict(
            args=[],
            kwargs=dict(value=None, type='uint32'),
            exp_obj=None,
        ),
        None, None, True
    ),
    (
        "Type is uint32, value is integer 0",
        dict(
            args=[],
            kwargs=dict(value=0, type='uint32'),
            exp_obj=Uint32(0),
        ),
        None, None, True
    ),
    (
        "Type is uint32, value is integer MAX_UINT32",
        dict(
            args=[],
            kwargs=dict(value=MAX_UINT32, type='uint32'),
            exp_obj=Uint32(MAX_UINT32),
        ),
        None, None, True
    ),
    (
        "Type is uint32, value is Uint32 0",
        dict(
            args=[],
            kwargs=dict(value=Uint32(0), type='uint32'),
            exp_obj=Uint32(0),
        ),
        None, None, True
    ),
    (
        "Type is uint32, value is Uint32 MAX_UINT32",
        dict(
            args=[],
            kwargs=dict(value=Uint32(MAX_UINT32), type='uint32'),
            exp_obj=Uint32(MAX_UINT32),
        ),
        None, None, True
    ),

    # Test cases where type is uint64
    (
        "Type is uint64, value is None",
        dict(
            args=[],
            kwargs=dict(value=None, type='uint64'),
            exp_obj=None,
        ),
        None, None, True
    ),
    (
        "Type is uint64, value is integer 0",
        dict(
            args=[],
            kwargs=dict(value=0, type='uint64'),
            exp_obj=Uint64(0),
        ),
        None, None, True
    ),
    (
        "Type is uint64, value is integer MAX_UINT64",
        dict(
            args=[],
            kwargs=dict(value=MAX_UINT64, type='uint64'),
            exp_obj=Uint64(MAX_UINT64),
        ),
        None, None, True
    ),
    (
        "Type is uint64, value is Uint64 0",
        dict(
            args=[],
            kwargs=dict(value=Uint64(0), type='uint64'),
            exp_obj=Uint64(0),
        ),
        None, None, True
    ),
    (
        "Type is uint64, value is Uint64 MAX_UINT64",
        dict(
            args=[],
            kwargs=dict(value=Uint64(MAX_UINT64), type='uint64'),
            exp_obj=Uint64(MAX_UINT64),
        ),
        None, None, True
    ),

    # Test cases where type is sint8
    (
        "Type is sint8, value is None",
        dict(
            args=[],
            kwargs=dict(value=None, type='sint8'),
            exp_obj=None,
        ),
        None, None, True
    ),
    (
        "Type is sint8, value is integer MIN_SINT8",
        dict(
            args=[],
            kwargs=dict(value=MIN_SINT8, type='sint8'),
            exp_obj=Sint8(MIN_SINT8),
        ),
        None, None, True
    ),
    (
        "Type is sint8, value is integer MAX_SINT8",
        dict(
            args=[],
            kwargs=dict(value=MAX_SINT8, type='sint8'),
            exp_obj=Sint8(MAX_SINT8),
        ),
        None, None, True
    ),
    (
        "Type is sint8, value is Sint8 MIN_SINT8",
        dict(
            args=[],
            kwargs=dict(value=Sint8(MIN_SINT8), type='sint8'),
            exp_obj=Sint8(MIN_SINT8),
        ),
        None, None, True
    ),
    (
        "Type is sint8, value is Sint8 MAX_SINT8",
        dict(
            args=[],
            kwargs=dict(value=Sint8(MAX_SINT8), type='sint8'),
            exp_obj=Sint8(MAX_SINT8),
        ),
        None, None, True
    ),

    # Test cases where type is sint16
    (
        "Type is sint16, value is None",
        dict(
            args=[],
            kwargs=dict(value=None, type='sint16'),
            exp_obj=None,
        ),
        None, None, True
    ),
    (
        "Type is sint16, value is integer MIN_SINT16",
        dict(
            args=[],
            kwargs=dict(value=MIN_SINT16, type='sint16'),
            exp_obj=Sint16(MIN_SINT16),
        ),
        None, None, True
    ),
    (
        "Type is sint16, value is integer MAX_SINT16",
        dict(
            args=[],
            kwargs=dict(value=MAX_SINT16, type='sint16'),
            exp_obj=Sint16(MAX_SINT16),
        ),
        None, None, True
    ),
    (
        "Type is sint16, value is Sint16 MIN_SINT16",
        dict(
            args=[],
            kwargs=dict(value=Sint16(MIN_SINT16), type='sint16'),
            exp_obj=Sint16(MIN_SINT16),
        ),
        None, None, True
    ),
    (
        "Type is sint16, value is Sint16 MAX_SINT16",
        dict(
            args=[],
            kwargs=dict(value=Sint16(MAX_SINT16), type='sint16'),
            exp_obj=Sint16(MAX_SINT16),
        ),
        None, None, True
    ),

    # Test cases where type is sint32
    (
        "Type is sint32, value is None",
        dict(
            args=[],
            kwargs=dict(value=None, type='sint32'),
            exp_obj=None,
        ),
        None, None, True
    ),
    (
        "Type is sint32, value is integer MIN_SINT32",
        dict(
            args=[],
            kwargs=dict(value=MIN_SINT32, type='sint32'),
            exp_obj=Sint32(MIN_SINT32),
        ),
        None, None, True
    ),
    (
        "Type is sint32, value is integer MAX_SINT32",
        dict(
            args=[],
            kwargs=dict(value=MAX_SINT32, type='sint32'),
            exp_obj=Sint32(MAX_SINT32),
        ),
        None, None, True
    ),
    (
        "Type is sint32, value is Sint32 MIN_SINT32",
        dict(
            args=[],
            kwargs=dict(value=Sint32(MIN_SINT32), type='sint32'),
            exp_obj=Sint32(MIN_SINT32),
        ),
        None, None, True
    ),
    (
        "Type is sint32, value is Sint32 MAX_SINT32",
        dict(
            args=[],
            kwargs=dict(value=Sint32(MAX_SINT32), type='sint32'),
            exp_obj=Sint32(MAX_SINT32),
        ),
        None, None, True
    ),

    # Test cases where type is sint64
    (
        "Type is sint64, value is None",
        dict(
            args=[],
            kwargs=dict(value=None, type='sint64'),
            exp_obj=None,
        ),
        None, None, True
    ),
    (
        "Type is sint64, value is integer MIN_SINT64",
        dict(
            args=[],
            kwargs=dict(value=MIN_SINT64, type='sint64'),
            exp_obj=Sint64(MIN_SINT64),
        ),
        None, None, True
    ),
    (
        "Type is sint64, value is integer MAX_SINT64",
        dict(
            args=[],
            kwargs=dict(value=MAX_SINT64, type='sint64'),
            exp_obj=Sint64(MAX_SINT64),
        ),
        None, None, True
    ),
    (
        "Type is sint64, value is Sint64 MIN_SINT64",
        dict(
            args=[],
            kwargs=dict(value=Sint64(MIN_SINT64), type='sint64'),
            exp_obj=Sint64(MIN_SINT64),
        ),
        None, None, True
    ),
    (
        "Type is sint64, value is Sint64 MAX_SINT64",
        dict(
            args=[],
            kwargs=dict(value=Sint64(MAX_SINT64), type='sint64'),
            exp_obj=Sint64(MAX_SINT64),
        ),
        None, None, True
    ),

    # Test cases where type is real32
    (
        "Type is real32, value is None",
        dict(
            args=[],
            kwargs=dict(value=None, type='real32'),
            exp_obj=None,
        ),
        None, None, True
    ),
    (
        "Type is real32, value is integer 0",
        dict(
            args=[],
            kwargs=dict(value=0, type='real32'),
            exp_obj=Real32(0),
        ),
        None, None, True
    ),
    (
        "Type is real32, value is positive float",
        dict(
            args=[],
            kwargs=dict(value=3.14, type='real32'),
            exp_obj=Real32(3.14),
        ),
        None, None, True
    ),
    (
        "Type is real32, value is negative integer",
        dict(
            args=[],
            kwargs=dict(value=-42E7, type='real32'),
            exp_obj=Real32(-42E7),
        ),
        None, None, True
    ),
    (
        "Type is real32, value is negative float",
        dict(
            args=[],
            kwargs=dict(value=-42E-7, type='real32'),
            exp_obj=Real32(-42E-7),
        ),
        None, None, True
    ),

    # Test cases where type is real64
    (
        "Type is real64, value is None",
        dict(
            args=[],
            kwargs=dict(value=None, type='real64'),
            exp_obj=None,
        ),
        None, None, True
    ),
    (
        "Type is real64, value is integer 0",
        dict(
            args=[],
            kwargs=dict(value=0, type='real64'),
            exp_obj=Real64(0),
        ),
        None, None, True
    ),
    (
        "Type is real64, value is positive float",
        dict(
            args=[],
            kwargs=dict(value=3.14, type='real64'),
            exp_obj=Real64(3.14),
        ),
        None, None, True
    ),
    (
        "Type is real64, value is positive integer",
        dict(
            args=[],
            kwargs=dict(value=-42E7, type='real64'),
            exp_obj=Real64(-42E7),
        ),
        None, None, True
    ),
    (
        "Type is real64, value is negative float",
        dict(
            args=[],
            kwargs=dict(value=-42E-7, type='real64'),
            exp_obj=Real64(-42E-7),
        ),
        None, None, True
    ),

    # Test cases where type is datetime
    (
        "Type is datetime, value is None",
        dict(
            args=[],
            kwargs=dict(value=None, type='datetime'),
            exp_obj=None,
        ),
        None, None, True
    ),
    (
        "Type is datetime, value is datetime object (point in time)",
        dict(
            args=[],
            kwargs=dict(value=DATETIME1_DT, type='datetime'),
            exp_obj=DATETIME1_OBJ,
        ),
        None, None, True
    ),
    (
        "Type is datetime, value is datetime string (point in time)",
        dict(
            args=[],
            kwargs=dict(value=DATETIME1_STR, type='datetime'),
            exp_obj=DATETIME1_OBJ,
        ),
        None, None, True
    ),
    (
        "Type is datetime, value is CIMDateTime object (point in time)",
        dict(
            args=[],
            kwargs=dict(value=DATETIME1_OBJ, type='datetime'),
            exp_obj=DATETIME1_OBJ,
        ),
        None, None, True
    ),
    (
        "Type is datetime, value is timedelta object (interval)",
        dict(
            args=[],
            kwargs=dict(value=TIMEDELTA1_TD, type='datetime'),
            exp_obj=TIMEDELTA1_OBJ,
        ),
        None, None, True
    ),
    (
        "Type is datetime, value is datetime string (interval)",
        dict(
            args=[],
            kwargs=dict(value=TIMEDELTA1_STR, type='datetime'),
            exp_obj=TIMEDELTA1_OBJ,
        ),
        None, None, True
    ),
    (
        "Type is datetime, value is CIMDateTime object (interval)",
        dict(
            args=[],
            kwargs=dict(value=TIMEDELTA1_OBJ, type='datetime'),
            exp_obj=TIMEDELTA1_OBJ,
        ),
        None, None, True
    ),

    # Test cases where type is reference
    (
        "Type is reference, value is None",
        dict(
            args=[],
            kwargs=dict(value=None, type='reference'),
            exp_obj=None,
        ),
        None, None, True
    ),
    (
        "Type is reference, value is reference string",
        dict(
            args=[],
            kwargs=dict(value=REF1_STR, type='reference'),
            exp_obj=REF1_OBJ,
        ),
        None, None, True
    ),
    (
        "Type is reference, value is CIMInstanceName object 1",
        dict(
            args=[],
            kwargs=dict(value=REF1_OBJ, type='reference'),
            exp_obj=REF1_OBJ,
        ),
        None, None, True
    ),
    (
        "Type is reference, value is CIMInstanceName object 2",
        dict(
            args=[],
            kwargs=dict(value=REF2_OBJ, type='reference'),
            exp_obj=REF2_OBJ,
        ),
        None, None, True
    ),

    # Exception testcases
    (
        "No type, plain integer value",
        dict(
            args=[],
            kwargs=dict(value=42, type=None),
            exp_obj=None,
        ),
        TypeError, None, True
    ),
    (
        "No type, plain negative integer value",
        dict(
            args=[],
            kwargs=dict(value=-42, type=None),
            exp_obj=None,
        ),
        TypeError, None, True
    ),
    (
        "No type, plain integer value 0",
        dict(
            args=[],
            kwargs=dict(value=0, type=None),
            exp_obj=None,
        ),
        TypeError, None, True
    ),
    (
        "No type, plain float value",
        dict(
            args=[],
            kwargs=dict(value=42.1, type=None),
            exp_obj=None,
        ),
        TypeError, None, True
    ),
    (
        "Invalid CIM type name",
        dict(
            args=[],
            kwargs=dict(value='abc', type='foo'),
            exp_obj=None,
        ),
        ValueError, None, True
    ),
    (
        "Type datetime, invalid datetime value",
        dict(
            args=[],
            kwargs=dict(value='000000:000', type='datetime'),
            exp_obj=None,
        ),
        ValueError, None, True
    ),
    (
        "Type reference, invalid reference value",
        dict(
            args=[],
            kwargs=dict(value='foo', type='reference'),
            exp_obj=None,
        ),
        ValueError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_CIMVALUE)
@simplified_test_function
@log_entry_exit
def test_cimvalue(testcase, args, kwargs, exp_obj):
    """
    Test function for cimvalue()
    """

    # The code to be tested
    obj = cimvalue(*args, **kwargs)

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

    assert obj == exp_obj


TESTCASES_MOFSTR = [

    # Testcases for mofstr()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * args: List of positional arguments for mofstr(), see kwargs.
    #   * kwargs: Dict of keyword arguments for mofstr():
    #     - value: Value to be tested.
    #     - indent: Indentation for new lines.
    #     - maxline: Maximum line length.
    #     - line_pos: Length of content already on the current line.
    #     - end_space: Length of space to be left free on the last line.
    #     - avoid_splits: Avoid splits at the price of starting a new line.
    #     - quote_char: Character to be used for surrounding the string parts.
    #   * exp_result: Expected result tuple, or None if failed.
    # * 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

    # Order of positional arguments
    (
        "Verify order of positional arguments",
        dict(
            args=[
                'abcdefgh',
                4,
                15,
                2,
                2,
                False,
                '"',
            ],
            kwargs={},
            exp_result=('"abcdefgh"', 12),
        ),
        None, None, True,
    ),

    # Some simple cases
    (
        "Empty string",
        dict(
            args=[],
            kwargs=dict(value=''),
            exp_result=('""', 2),
        ),
        None, None, True
    ),
    (
        "Single character",
        dict(
            args=[],
            kwargs=dict(value='c'),
            exp_result=('"c"', 3),
        ),
        None, None, True
    ),

    # Test whitespace preservation
    (
        "String with inside blank",
        dict(
            args=[],
            kwargs=dict(value='c d'),
            exp_result=('"c d"', 5),
        ),
        None, None, True
    ),
    (
        "String with leading blank",
        dict(
            args=[],
            kwargs=dict(value=' d'),
            exp_result=('" d"', 4),
        ),
        None, None, True
    ),
    (
        "String with trailing blank",
        dict(
            args=[],
            kwargs=dict(value='c '),
            exp_result=('"c "', 4),
        ),
        None, None, True
    ),
    (
        "String that is a blank",
        dict(
            args=[],
            kwargs=dict(value=' '),
            exp_result=('" "', 3),
        ),
        None, None, True
    ),
    (
        "String with two inside blanks",
        dict(
            args=[],
            kwargs=dict(value='c  d'),
            exp_result=('"c  d"', 6),
        ),
        None, None, True
    ),

    # Test single quote escaping
    (
        "String that is single quote",
        dict(
            args=[],
            kwargs=dict(value='\''),
            exp_result=('"\\\'"', 4),
        ),
        None, None, True
    ),
    (
        "String with inside single quote",
        dict(
            args=[],
            kwargs=dict(value='c\'d'),
            exp_result=('"c\\\'d"', 6),
        ),
        None, None, True
    ),
    (
        "String with two inside single quotes",
        dict(
            args=[],
            kwargs=dict(value='c\'\'d'),
            exp_result=('"c\\\'\\\'d"', 8),
        ),
        None, None, True
    ),

    # Test double quote escaping
    (
        "String that is double quote",
        dict(
            args=[],
            kwargs=dict(value='"'),
            exp_result=('"\\""', 4),
        ),
        None, None, True
    ),
    (
        "String with inside double quote",
        dict(
            args=[],
            kwargs=dict(value='c"d'),
            exp_result=('"c\\"d"', 6),
        ),
        None, None, True
    ),
    (
        "String with two inside double quotes",
        dict(
            args=[],
            kwargs=dict(value='c""d'),
            exp_result=('"c\\"\\"d"', 8),
        ),
        None, None, True
    ),

    # Test backslash escaping
    (
        "String that is backslash",
        dict(
            args=[],
            kwargs=dict(value='\\'),
            exp_result=('"\\\\"', 4),
        ),
        None, None, True
    ),
    (
        "String with leading backslash",
        dict(
            args=[],
            kwargs=dict(value='\\d'),
            exp_result=('"\\\\d"', 5),
        ),
        None, None, True
    ),
    (
        "String with trailing backslash",
        dict(
            args=[],
            kwargs=dict(value='c\\'),
            exp_result=('"c\\\\"', 5),
        ),
        None, None, True
    ),
    (
        "String with inside backslash",
        dict(
            args=[],
            kwargs=dict(value='c\\d'),
            exp_result=('"c\\\\d"', 6),
        ),
        None, None, True
    ),
    (
        "String with two inside backslashes",
        dict(
            args=[],
            kwargs=dict(value='c\\\\d'),
            exp_result=('"c\\\\\\\\d"', 8),
        ),
        None, None, True
    ),

    # Test MOF named escape sequences
    (
        "String that is escapable character \\b",
        dict(
            args=[],
            kwargs=dict(value='\b'),
            exp_result=('"\\b"', 4),
        ),
        None, None, True
    ),
    (
        "String that is escapable character \\t",
        dict(
            args=[],
            kwargs=dict(value='\t'),
            exp_result=('"\\t"', 4),
        ),
        None, None, True
    ),
    (
        "String that is escapable character \\n",
        dict(
            args=[],
            kwargs=dict(value='\n'),
            exp_result=('"\\n"', 4),
        ),
        None, None, True
    ),
    (
        "String that is escapable character \\f",
        dict(
            args=[],
            kwargs=dict(value='\f'),
            exp_result=('"\\f"', 4),
        ),
        None, None, True
    ),
    (
        "String that is escapable character \\r",
        dict(
            args=[],
            kwargs=dict(value='\r'),
            exp_result=('"\\r"', 4),
        ),
        None, None, True
    ),
    (
        "String with inside escapable character \\b",
        dict(
            args=[],
            kwargs=dict(value='c\bd'),
            exp_result=('"c\\bd"', 6),
        ),
        None, None, True
    ),
    (
        "String with inside escapable character \\t",
        dict(
            args=[],
            kwargs=dict(value='c\td'),
            exp_result=('"c\\td"', 6),
        ),
        None, None, True
    ),
    (
        "String with inside escapable character \\n",
        dict(
            args=[],
            kwargs=dict(value='c\nd'),
            exp_result=('"c\\nd"', 6),
        ),
        None, None, True
    ),
    (
        "String with inside escapable character \\f",
        dict(
            args=[],
            kwargs=dict(value='c\fd'),
            exp_result=('"c\\fd"', 6),
        ),
        None, None, True
    ),
    (
        "String with inside escapable character \\r",
        dict(
            args=[],
            kwargs=dict(value='c\rd'),
            exp_result=('"c\\rd"', 6),
        ),
        None, None, True
    ),

    # Test an already MOF-escaped sequence.
    # Such a sequence is treated as separate characters, i.e. backslash
    # gets escaped, and the following char gets escaped on its own.
    # These sequences were treated specially before v0.9, by parsing them
    # as already-escaped MOF sequences and passing them through unchanged.
    (
        "String that is escaped character \\b",
        dict(
            args=[],
            kwargs=dict(value='\\b'),
            exp_result=('"\\\\b"', 5),
        ),
        None, None, True
    ),
    (
        "String that is escaped character \\t",
        dict(
            args=[],
            kwargs=dict(value='\\t'),
            exp_result=('"\\\\t"', 5),
        ),
        None, None, True
    ),
    (
        "String that is escaped character \\n",
        dict(
            args=[],
            kwargs=dict(value='\\n'),
            exp_result=('"\\\\n"', 5),
        ),
        None, None, True
    ),
    (
        "String that is escaped character \\f",
        dict(
            args=[],
            kwargs=dict(value='\\f'),
            exp_result=('"\\\\f"', 5),
        ),
        None, None, True
    ),
    (
        "String that is escaped character \\r",
        dict(
            args=[],
            kwargs=dict(value='\\r'),
            exp_result=('"\\\\r"', 5),
        ),
        None, None, True
    ),
    (
        "String that is escaped character \' ",
        dict(
            args=[],
            kwargs=dict(value='\\\''),
            exp_result=('"\\\\\\\'"', 6),
        ),
        None, None, True
    ),
    (
        "String that is escaped character \" ",
        dict(
            args=[],
            kwargs=dict(value='\\"'),
            exp_result=('"\\\\\\""', 6),
        ),
        None, None, True
    ),
    (
        "String with inside escaped character \\b",
        dict(
            args=[],
            kwargs=dict(value='c\\bd'),
            exp_result=('"c\\\\bd"', 7),
        ),
        None, None, True
    ),
    (
        "String with inside escaped character \\t",
        dict(
            args=[],
            kwargs=dict(value='c\\td'),
            exp_result=('"c\\\\td"', 7),
        ),
        None, None, True
    ),
    (
        "String with inside escaped character \\n",
        dict(
            args=[],
            kwargs=dict(value='c\\nd'),
            exp_result=('"c\\\\nd"', 7),
        ),
        None, None, True
    ),
    (
        "String with inside escaped character \\f",
        dict(
            args=[],
            kwargs=dict(value='c\\fd'),
            exp_result=('"c\\\\fd"', 7),
        ),
        None, None, True
    ),
    (
        "String with inside escaped character \\r",
        dict(
            args=[],
            kwargs=dict(value='c\\rd'),
            exp_result=('"c\\\\rd"', 7),
        ),
        None, None, True
    ),
    (
        "String with inside escaped character \' ",
        dict(
            args=[],
            kwargs=dict(value='c\\\'d'),
            exp_result=('"c\\\\\\\'d"', 8),
        ),
        None, None, True
    ),
    (
        "String with inside escaped character \" ",
        dict(
            args=[],
            kwargs=dict(value='c\\"d'),
            exp_result=('"c\\\\\\"d"', 8),
        ),
        None, None, True
    ),

    # Test backslash followed by MOF-escapable character
    (
        "String with backslash and escapable character \\b",
        dict(
            args=[],
            kwargs=dict(value='\\\b'),
            exp_result=('"\\\\\\b"', 6),
        ),
        None, None, True
    ),
    (
        "String with backslash and escapable character \\t",
        dict(
            args=[],
            kwargs=dict(value='\\\t'),
            exp_result=('"\\\\\\t"', 6),
        ),
        None, None, True
    ),
    (
        "String with backslash and escapable character \\n",
        dict(
            args=[],
            kwargs=dict(value='\\\n'),
            exp_result=('"\\\\\\n"', 6),
        ),
        None, None, True
    ),
    (
        "String with backslash and escapable character \\f",
        dict(
            args=[],
            kwargs=dict(value='\\\f'),
            exp_result=('"\\\\\\f"', 6),
        ),
        None, None, True
    ),
    (
        "String with backslash and escapable character \\r",
        dict(
            args=[],
            kwargs=dict(value='\\\r'),
            exp_result=('"\\\\\\r"', 6),
        ),
        None, None, True
    ),
    (
        "String with inside backslash and escapable character \\b",
        dict(
            args=[],
            kwargs=dict(value='c\\\bd'),
            exp_result=('"c\\\\\\bd"', 8),
        ),
        None, None, True
    ),
    (
        "String with inside backslash and escapable character \\t",
        dict(
            args=[],
            kwargs=dict(value='c\\\td'),
            exp_result=('"c\\\\\\td"', 8),
        ),
        None, None, True
    ),
    (
        "String with inside backslash and escapable character \\n",
        dict(
            args=[],
            kwargs=dict(value='c\\\nd'),
            exp_result=('"c\\\\\\nd"', 8),
        ),
        None, None, True
    ),
    (
        "String with inside backslash and escapable character \\f",
        dict(
            args=[],
            kwargs=dict(value='c\\\fd'),
            exp_result=('"c\\\\\\fd"', 8),
        ),
        None, None, True
    ),
    (
        "String with inside backslash and escapable character \\r",
        dict(
            args=[],
            kwargs=dict(value='c\\\rd'),
            exp_result=('"c\\\\\\rd"', 8),
        ),
        None, None, True
    ),

    # Test control characters
    (
        "String that is control character U+0001",
        dict(
            args=[],
            kwargs=dict(value='\u0001'),
            exp_result=('"\\x0001"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+0002",
        dict(
            args=[],
            kwargs=dict(value='\u0002'),
            exp_result=('"\\x0002"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+0003",
        dict(
            args=[],
            kwargs=dict(value='\u0003'),
            exp_result=('"\\x0003"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+0004",
        dict(
            args=[],
            kwargs=dict(value='\u0004'),
            exp_result=('"\\x0004"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+0005",
        dict(
            args=[],
            kwargs=dict(value='\u0005'),
            exp_result=('"\\x0005"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+0006",
        dict(
            args=[],
            kwargs=dict(value='\u0006'),
            exp_result=('"\\x0006"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+0007",
        dict(
            args=[],
            kwargs=dict(value='\u0007'),
            exp_result=('"\\x0007"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+0008",
        dict(
            args=[],
            kwargs=dict(value='\u0008'),
            exp_result=('"\\b"', 4),
        ),
        None, None, True
    ),
    (
        "String that is control character U+0009",
        dict(
            args=[],
            kwargs=dict(value='\u0009'),
            exp_result=('"\\t"', 4),
        ),
        None, None, True
    ),
    (
        "String that is control character U+000A",
        dict(
            args=[],
            kwargs=dict(value='\u000A'),
            exp_result=('"\\n"', 4),
        ),
        None, None, True
    ),
    (
        "String that is control character U+000B",
        dict(
            args=[],
            kwargs=dict(value='\u000B'),
            exp_result=('"\\x000B"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+000C",
        dict(
            args=[],
            kwargs=dict(value='\u000C'),
            exp_result=('"\\f"', 4),
        ),
        None, None, True
    ),
    (
        "String that is control character U+000D",
        dict(
            args=[],
            kwargs=dict(value='\u000D'),
            exp_result=('"\\r"', 4),
        ),
        None, None, True
    ),
    (
        "String that is control character U+000E",
        dict(
            args=[],
            kwargs=dict(value='\u000E'),
            exp_result=('"\\x000E"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+000F",
        dict(
            args=[],
            kwargs=dict(value='\u000F'),
            exp_result=('"\\x000F"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+0010",
        dict(
            args=[],
            kwargs=dict(value='\u0010'),
            exp_result=('"\\x0010"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+0011",
        dict(
            args=[],
            kwargs=dict(value='\u0011'),
            exp_result=('"\\x0011"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+0012",
        dict(
            args=[],
            kwargs=dict(value='\u0012'),
            exp_result=('"\\x0012"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+0013",
        dict(
            args=[],
            kwargs=dict(value='\u0013'),
            exp_result=('"\\x0013"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+0014",
        dict(
            args=[],
            kwargs=dict(value='\u0014'),
            exp_result=('"\\x0014"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+0015",
        dict(
            args=[],
            kwargs=dict(value='\u0015'),
            exp_result=('"\\x0015"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+0016",
        dict(
            args=[],
            kwargs=dict(value='\u0016'),
            exp_result=('"\\x0016"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+0017",
        dict(
            args=[],
            kwargs=dict(value='\u0017'),
            exp_result=('"\\x0017"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+0018",
        dict(
            args=[],
            kwargs=dict(value='\u0018'),
            exp_result=('"\\x0018"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+0019",
        dict(
            args=[],
            kwargs=dict(value='\u0019'),
            exp_result=('"\\x0019"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+001A",
        dict(
            args=[],
            kwargs=dict(value='\u001A'),
            exp_result=('"\\x001A"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+001B",
        dict(
            args=[],
            kwargs=dict(value='\u001B'),
            exp_result=('"\\x001B"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+001C",
        dict(
            args=[],
            kwargs=dict(value='\u001C'),
            exp_result=('"\\x001C"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+001D",
        dict(
            args=[],
            kwargs=dict(value='\u001D'),
            exp_result=('"\\x001D"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+001E",
        dict(
            args=[],
            kwargs=dict(value='\u001E'),
            exp_result=('"\\x001E"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+001F",
        dict(
            args=[],
            kwargs=dict(value='\u001F'),
            exp_result=('"\\x001F"', 8),
        ),
        None, None, True
    ),
    (
        "String that is control character U+0020",
        dict(
            args=[],
            kwargs=dict(value='\u0020'),
            exp_result=('" "', 3),
        ),
        None, None, True
    ),

    # Test line break cases, starting at begin of line
    (
        "Line break with split on a space between words",
        dict(
            args=[],
            kwargs=dict(
                value='the big brown fox1 jumps over a big brown '
                'fox2 jumps over a big brown fox3',
                maxline=56
            ),
            exp_result=(
                '"the big brown fox1 jumps over a big brown fox2 jumps "\n'
                '   "over a big brown fox3"',
                26
            ),
        ),
        None, None, True
    ),
    (
        "Line break with split in a long word with no blanks",
        dict(
            args=[],
            kwargs=dict(
                value='the_big_brown_fox1_jumps_over_a_big_brown_fox2_jumps_'
                'over_a_big_brown_fox3',
                maxline=56
            ),
            exp_result=(
                '\n'
                '   "the_big_brown_fox1_jumps_over_a_big_brown_fox2_jump"\n'
                '   "s_over_a_big_brown_fox3"',
                28
            ),
        ),
        None, None, True
    ),

    # Test initial line position on words without blanks
    (
        "No blanks, Line position without new line",
        dict(
            args=[],
            kwargs=dict(value='abc', line_pos=8),
            exp_result=('"abc"', 13),
        ),
        None, None, True
    ),
    (
        "No blanks, Line position with new line using max",
        dict(
            args=[],
            kwargs=dict(
                value='abcd_fghi_abcd_fghi_ab',
                line_pos=5, maxline=15, indent=2
            ),
            exp_result=('\n  "abcd_fghi_a"\n  "bcd_fghi_ab"', 15),
        ),
        None, None, True
    ),
    (
        "No blanks, Line position with new line using max - 1",
        dict(
            args=[],
            kwargs=dict(
                value='abcd_fghi_abcd_fghi_a',
                line_pos=5, maxline=15, indent=2
            ),
            exp_result=('\n  "abcd_fghi_a"\n  "bcd_fghi_a"', 14),
        ),
        None, None, True
    ),
    (
        "No blanks, Line position with new line using max + 1",
        dict(
            args=[],
            kwargs=dict(
                value='abcd_fghi_abcd_fghi_abc',
                line_pos=5, maxline=15, indent=2
            ),
            exp_result=('\n  "abcd_fghi_a"\n  "bcd_fghi_ab"\n  "c"', 5),
        ),
        None, None, True
    ),
    (
        "No blanks, Line position starting at maxline",
        dict(
            args=[],
            kwargs=dict(
                value='abcd_fghi',
                line_pos=15, maxline=15, indent=2
            ),
            exp_result=('\n  "abcd_fghi"', 13),
        ),
        None, None, True
    ),

    # Test initial line position on words with blanks
    (
        "Blanks, Line position with new line using max",
        dict(
            args=[],
            kwargs=dict(
                value='abcd fghij lmnop',
                line_pos=5, maxline=15, indent=2
            ),
            exp_result=('"abcd "\n  "fghij lmnop"', 15),
        ),
        None, None, True
    ),
    (
        "Blanks, Line position with new line using max - 1",
        dict(
            args=[],
            kwargs=dict(
                value='abcd fghij lmno',
                line_pos=5, maxline=15, indent=2
            ),
            exp_result=('"abcd "\n  "fghij lmno"', 14),
        ),
        None, None, True
    ),
    (
        "Blanks, Line position with new line using max + 1",
        dict(
            args=[],
            kwargs=dict(
                value='abcd fghij lmnopq',
                line_pos=5, maxline=15, indent=2
            ),
            exp_result=('"abcd "\n  "fghij "\n  "lmnopq"', 10),
        ),
        None, None, True
    ),
    (
        "Blanks, Line position starting at maxline",
        dict(
            args=[],
            kwargs=dict(
                value='abcd fghi',
                line_pos=15, maxline=15, indent=2
            ),
            exp_result=('\n  "abcd fghi"', 13),
        ),
        None, None, True
    ),
    (
        "Blanks, Line position starting to exactly fit first word",
        dict(
            args=[],
            kwargs=dict(
                value='abcd fghi',
                line_pos=8, maxline=15, indent=2
            ),
            exp_result=('"abcd "\n  "fghi"', 8),
        ),
        None, None, True
    ),
    (
        "See issue #343",
        dict(
            args=[],
            kwargs=dict(
                value='Linux version 3.13.0-86-generic (buildd@lgw01-51) '
                '(gcc version 4.8.2',
                line_pos=18, maxline=80, indent=8
            ),
            exp_result=(
                '"Linux version 3.13.0-86-generic (buildd@lgw01-51) (gcc "\n'
                '        "version 4.8.2"',
                23
            ),
        ),
        None, None, True
    ),

    # Test end space on words with blanks
    (
        "Blanks, End space with new line using max",
        dict(
            args=[],
            kwargs=dict(
                value='abcd fghij lmn',
                line_pos=5, maxline=15, indent=2, end_space=2
            ),
            exp_result=('"abcd "\n  "fghij lmn"', 13),
        ),
        None, None, True
    ),
    (
        "Blanks, End space with new line using max - 1",
        dict(
            args=[],
            kwargs=dict(
                value='abcd fghij lm',
                line_pos=5, maxline=15, indent=2, end_space=2
            ),
            exp_result=('"abcd "\n  "fghij lm"', 12),
        ),
        None, None, True
    ),
    (
        "Blanks, End space with new line using max + 1",
        dict(
            args=[],
            kwargs=dict(
                value='abcd fghij lmno',
                line_pos=5, maxline=15, indent=2, end_space=2
            ),
            exp_result=('"abcd "\n  "fghij "\n  "lmno"', 8),
        ),
        None, None, True
    ),
    (
        "Blanks, End space with new line using max + 1, on first part",
        dict(
            args=[],
            kwargs=dict(
                value='abcdefgh',
                line_pos=4, maxline=15, indent=2, end_space=2
            ),
            exp_result=('\n  "abcdefgh"', 12),
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_MOFSTR)
@simplified_test_function
@log_entry_exit
def test_mofstr(testcase, args, kwargs, exp_result):
    """
    Test function for mofstr()
    """

    # The code to be tested
    result = mofstr(*args, **kwargs)

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

    exp_mof, exp_pos = exp_result
    mof, pos = result
    assert isinstance(mof, str)
    assert mof == exp_mof
    assert isinstance(pos, int)
    assert pos == exp_pos
