Skip to content

Commit

Permalink
Improved testcases for CIMInstanceName with different key binding order.
Browse files Browse the repository at this point in the history
Details:
- Added a test that attempts to establish two CIMInstancename objects
  with different iteration order of their key bindings, and then
  tests for equality.
- Removed the sorting of key bindings in CIMInstanceName.__repr__().
  • Loading branch information
andy-maier committed Apr 24, 2017
1 parent b777bb8 commit f9bfea5
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 3 deletions.
7 changes: 7 additions & 0 deletions docs/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ Enhancements

See issue #761

* Changed `CIMInstancename.__repr__()` to show the key bindings in the
iteration order, and no longer in sorted order, to better debug
iteration order related issues. See issue #585.

Bug fixes
^^^^^^^^^

Expand Down Expand Up @@ -105,6 +109,9 @@ Build, test, quality
* Fixed TypeError about dict ordering on Python 3.6 in unit test
'test_nocasedict.TestOrdering' (issue #661).

* Added a testcase for `CIMInstanceName` to compare two objects with
different ordering of their key bindings for equality. See issue #686.

Documentation
^^^^^^^^^^^^^

Expand Down
2 changes: 1 addition & 1 deletion pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ persistent=yes
load-plugins=

# Use multiple processes to speed up Pylint.
jobs=1
jobs=4

# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
Expand Down
5 changes: 3 additions & 2 deletions pywbem/cim_obj.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,10 +333,11 @@ def __repr__(self):
"""
Return a string representation of the `NocaseDict`_ object that is
suitable for debugging.
"""
The order of dictionary items is the iteration order of `iteritems()`.
"""
items = ', '.join([('%r: %r' % (key, value))
for key, value in sorted(self.iteritems())])
for key, value in self.iteritems()])
return 'NocaseDict({%s})' % items

def update(self, *args, **kwargs):
Expand Down
65 changes: 65 additions & 0 deletions testsuite/test_cim_obj.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,71 @@ def test_all(self):
self.assertEqual(CIMInstanceName('CIM_Foo', namespace='root/cimv2'),
CIMInstanceName('CIM_Foo', namespace='Root/CIMv2'))

def test_keybindings_order(self):
"""
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
http://stackoverflow.com/a/15479974/1424462.
"""

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

d1 = {key1: value1, key2: value2}
kb1 = NocaseDict(d1)
obj1 = CIMInstanceName('CIM_Foo', kb1)

d2 = {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(" Hash values of keys: k1=%r (hash: %s), k2=%r (hash: %s)" %
(key1, hash(key1) % 8, key2, hash(key2) % 8))
print(" First keys: k1=%r, k2=%r" % (k1_first, k2_first))
print(" Input dicts: d1=%r, d2=%r" % (d1, d2))
print(" Input key bindings: kb1=%r, kb2=%r" % (kb1, kb2))
print(" Object key bindings: obj1.kb=%r, obj2.kb=%r" %
(obj1.keybindings, obj2.keybindings))
print(" Objects:\n obj1=%r\n obj2=%r" % (obj1, obj2))
if obj1 != obj2:
raise AssertionError(
"CIMInstanceName objects with different iteration order of "
"key bindings do not compare equal:\n"
" obj1=%r\n"
" obj2=%r" % (obj1, obj2))


class CIMInstanceNameCompare(unittest.TestCase):
"""
Expand Down

0 comments on commit f9bfea5

Please sign in to comment.