Skip to content

Commit 059e23a

Browse files
committed
Coverage: 100%
1 parent 4f09418 commit 059e23a

File tree

3 files changed

+125
-12
lines changed

3 files changed

+125
-12
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@ __pycache__/
1717
/dist
1818
/.cache
1919
/.pytest_cache
20+
/.coverage
21+

immutables/map.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@
33
import sys
44

55

6+
__all__ = ('Map',)
7+
8+
9+
# Python version of _map.c. The topmost comment there explains
10+
# all datastructures and algorithms.
11+
# The code here follows C code closely on purpose to make
12+
# debugging and testing easier.
13+
14+
615
def map_hash(o):
716
x = hash(o)
817
return (x & 0xffffffff) ^ ((x >> 32) & 0xffffffff)
@@ -141,7 +150,7 @@ def without(self, shift, hash, key):
141150
res, sub_node = val_or_node.without(shift + 5, hash, key)
142151

143152
if res is W_EMPTY:
144-
raise RuntimeError('unreachable code')
153+
raise RuntimeError('unreachable code') # pragma: no cover
145154

146155
elif res is W_NEWNODE:
147156
if (type(sub_node) is BitmapNode and
@@ -162,6 +171,9 @@ def without(self, shift, hash, key):
162171

163172
else:
164173
if key == key_or_null:
174+
if self.size == 2:
175+
return W_EMPTY, None
176+
165177
new_array = self.array[:key_idx]
166178
new_array.extend(self.array[val_idx + 1:])
167179
new_node = BitmapNode(
@@ -201,7 +213,7 @@ def items(self):
201213
else:
202214
yield key_or_null, val_or_node
203215

204-
def dump(self, buf, level):
216+
def dump(self, buf, level): # pragma: no cover
205217
buf.append(
206218
' ' * (level + 1) +
207219
'BitmapNode(size={} count={} bitmap={} id={:0x}):'.format(
@@ -274,7 +286,8 @@ def without(self, shift, hash, key):
274286

275287
new_size = self.size - 2
276288
if new_size == 0:
277-
return W_EMPTY, None
289+
# Shouldn't be ever reachable
290+
return W_EMPTY, None # pragma: no cover
278291

279292
if new_size == 2:
280293
if key_idx == 0:
@@ -303,7 +316,7 @@ def items(self):
303316
for i in range(0, self.size, 2):
304317
yield self.array[i], self.array[i + 1]
305318

306-
def dump(self, buf, level):
319+
def dump(self, buf, level): # pragma: no cover
307320
pad = ' ' * (level + 1)
308321
buf.append(
309322
pad + 'CollisionNode(size={} id={:0x}):'.format(
@@ -445,9 +458,9 @@ def __hash__(self):
445458
h &= MASK
446459

447460
if h > MAX:
448-
h -= MASK + 1
461+
h -= MASK + 1 # pragma: no cover
449462
if h == -1:
450-
h = 590923713
463+
h = 590923713 # pragma: no cover
451464

452465
self.__hash = h
453466
return h
@@ -460,7 +473,7 @@ def __repr__(self):
460473
return '<immutables.Map({{{}}}) at 0x{:0x}>'.format(
461474
', '.join(items), id(self))
462475

463-
def __dump__(self):
476+
def __dump__(self): # pragma: no cover
464477
buf = []
465478
self.__root.dump(buf, 0)
466479
return '\n'.join(buf)

tests/test_map.py

Lines changed: 103 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,50 @@ def test_map_collision_1(self):
194194
self.assertEqual(len(h4), 2)
195195
self.assertEqual(len(h5), 3)
196196

197+
def test_map_collision_2(self):
198+
A = HashKey(100, 'A')
199+
B = HashKey(101, 'B')
200+
C = HashKey(0b011000011100000100, 'C')
201+
D = HashKey(0b011000011100000100, 'D')
202+
E = HashKey(0b1011000011100000100, 'E')
203+
204+
h = self.Map()
205+
h = h.set(A, 'a')
206+
h = h.set(B, 'b')
207+
h = h.set(C, 'c')
208+
h = h.set(D, 'd')
209+
210+
# BitmapNode(size=6 bitmap=0b100110000):
211+
# NULL:
212+
# BitmapNode(size=4 bitmap=0b1000000000000000000001000):
213+
# <Key name:A hash:100>: 'a'
214+
# NULL:
215+
# CollisionNode(size=4 id=0x108572410):
216+
# <Key name:C hash:100100>: 'c'
217+
# <Key name:D hash:100100>: 'd'
218+
# <Key name:B hash:101>: 'b'
219+
220+
h = h.set(E, 'e')
221+
222+
# BitmapNode(size=4 count=2.0 bitmap=0b110000 id=10b8ea5c0):
223+
# None:
224+
# BitmapNode(size=4 count=2.0
225+
# bitmap=0b1000000000000000000001000 id=10b8ea518):
226+
# <Key name:A hash:100>: 'a'
227+
# None:
228+
# BitmapNode(size=2 count=1.0 bitmap=0b10
229+
# id=10b8ea4a8):
230+
# None:
231+
# BitmapNode(size=4 count=2.0
232+
# bitmap=0b100000001000
233+
# id=10b8ea4e0):
234+
# None:
235+
# CollisionNode(size=4 id=10b8ea470):
236+
# <Key name:C hash:100100>: 'c'
237+
# <Key name:D hash:100100>: 'd'
238+
# <Key name:E hash:362244>: 'e'
239+
# <Key name:B hash:101>: 'b'
240+
197241
def test_map_stress(self):
198242
COLLECTION_SIZE = 7000
199243
TEST_ITERS_EVERY = 647
@@ -296,6 +340,7 @@ def test_map_delete_1(self):
296340

297341
h = self.Map()
298342
h = h.set(A, 'a')
343+
h = h.set(A, 'a')
299344
h = h.set(B, 'b')
300345
h = h.set(C, 'c')
301346
h = h.set(D, 'd')
@@ -334,6 +379,7 @@ def test_map_delete_2(self):
334379
A = HashKey(100, 'A')
335380
B = HashKey(201001, 'B')
336381
C = HashKey(101001, 'C')
382+
BLike = HashKey(201001, 'B-like')
337383
D = HashKey(103, 'D')
338384
E = HashKey(104, 'E')
339385
Z = HashKey(-100, 'Z')
@@ -347,6 +393,11 @@ def test_map_delete_2(self):
347393
h = h.set(D, 'd')
348394
h = h.set(E, 'e')
349395

396+
h = h.set(B, 'b') # trigger branch in BitmapNode.assoc
397+
398+
with self.assertRaises(KeyError):
399+
h.delete(BLike) # trigger branch in BitmapNode.without
400+
350401
orig_len = len(h)
351402

352403
# BitmapNode(size=8 bitmap=0b1110010000):
@@ -387,11 +438,15 @@ def test_map_delete_2(self):
387438
self.assertEqual(len(h), 0)
388439

389440
def test_map_delete_3(self):
390-
A = HashKey(100, 'A')
391-
B = HashKey(101, 'B')
392-
C = HashKey(100100, 'C')
393-
D = HashKey(100100, 'D')
394-
E = HashKey(104, 'E')
441+
A = HashKey(0b00000000001100100, 'A')
442+
B = HashKey(0b00000000001100101, 'B')
443+
444+
C = HashKey(0b11000011100000100, 'C')
445+
D = HashKey(0b11000011100000100, 'D')
446+
X = HashKey(0b01000011100000100, 'Z')
447+
Y = HashKey(0b11000011100000100, 'Y')
448+
449+
E = HashKey(0b00000000001101000, 'E')
395450

396451
h = self.Map()
397452
h = h.set(A, 'a')
@@ -400,8 +455,15 @@ def test_map_delete_3(self):
400455
h = h.set(D, 'd')
401456
h = h.set(E, 'e')
402457

458+
h = h.set(C, 'c') # trigger branch in CollisionNode.assoc
459+
403460
orig_len = len(h)
404461

462+
with self.assertRaises(KeyError):
463+
h.delete(X)
464+
with self.assertRaises(KeyError):
465+
h.delete(Y)
466+
405467
# BitmapNode(size=6 bitmap=0b100110000):
406468
# NULL:
407469
# BitmapNode(size=4 bitmap=0b1000000000000000000001000):
@@ -422,6 +484,14 @@ def test_map_delete_3(self):
422484
self.assertEqual(h.get(C), 'c')
423485
self.assertEqual(h.get(B), 'b')
424486

487+
h2 = h.delete(C)
488+
self.assertEqual(len(h2), orig_len - 3)
489+
490+
h2 = h.delete(D)
491+
self.assertEqual(len(h2), orig_len - 3)
492+
493+
self.assertEqual(len(h), orig_len - 2)
494+
425495
def test_map_delete_4(self):
426496
A = HashKey(100, 'A')
427497
B = HashKey(101, 'B')
@@ -516,6 +586,13 @@ def test_map_delete_5(self):
516586
h = h.delete(key)
517587
self.assertEqual(len(h), 0)
518588

589+
def test_map_delete_6(self):
590+
h = self.Map()
591+
h = h.set(1, 1)
592+
h = h.delete(1)
593+
self.assertEqual(len(h), 0)
594+
self.assertEqual(h, self.Map())
595+
519596
def test_map_items_1(self):
520597
A = HashKey(100, 'A')
521598
B = HashKey(201001, 'B')
@@ -577,6 +654,24 @@ def test_map_keys_1(self):
577654
self.assertEqual(set(list(h.keys())), {A, B, C, D, E, F})
578655
self.assertEqual(set(list(h)), {A, B, C, D, E, F})
579656

657+
def test_map_values_1(self):
658+
A = HashKey(100, 'A')
659+
B = HashKey(101, 'B')
660+
C = HashKey(100100, 'C')
661+
D = HashKey(100100, 'D')
662+
E = HashKey(100100, 'E')
663+
F = HashKey(110, 'F')
664+
665+
h = self.Map()
666+
h = h.set(A, 'a')
667+
h = h.set(B, 'b')
668+
h = h.set(C, 'c')
669+
h = h.set(D, 'd')
670+
h = h.set(E, 'e')
671+
h = h.set(F, 'f')
672+
673+
self.assertEqual(set(list(h.values())), {'a', 'b', 'c', 'd', 'e', 'f'})
674+
580675
def test_map_items_3(self):
581676
h = self.Map()
582677
self.assertEqual(len(h.items()), 0)
@@ -645,6 +740,9 @@ def test_map_eq_2(self):
645740
with self.assertRaisesRegex(ValueError, 'cannot compare'):
646741
h1 != h2
647742

743+
def test_map_eq_3(self):
744+
self.assertNotEqual(self.Map(), 1)
745+
648746
def test_map_gc_1(self):
649747
A = HashKey(100, 'A')
650748

0 commit comments

Comments
 (0)