1# tests common to dict and UserDict
2import unittest
3import collections
4
5
6class BasicTestMappingProtocol(unittest.TestCase):
7    # This base class can be used to check that an object conforms to the
8    # mapping protocol
9
10    # Functions that can be useful to override to adapt to dictionary
11    # semantics
12    type2test = None # which class is being tested (overwrite in subclasses)
13
14    def _reference(self):
15        """Return a dictionary of values which are invariant by storage
16        in the object under test."""
17        return {"1": "2", "key1":"value1", "key2":(1,2,3)}
18    def _empty_mapping(self):
19        """Return an empty mapping object"""
20        return self.type2test()
21    def _full_mapping(self, data):
22        """Return a mapping object with the value contained in data
23        dictionary"""
24        x = self._empty_mapping()
25        for key, value in data.items():
26            x[key] = value
27        return x
28
29    def __init__(self, *args, **kw):
30        unittest.TestCase.__init__(self, *args, **kw)
31        self.reference = self._reference().copy()
32
33        # A (key, value) pair not in the mapping
34        key, value = self.reference.popitem()
35        self.other = {key:value}
36
37        # A (key, value) pair in the mapping
38        key, value = self.reference.popitem()
39        self.inmapping = {key:value}
40        self.reference[key] = value
41
42    def test_read(self):
43        # Test for read only operations on mapping
44        p = self._empty_mapping()
45        p1 = dict(p) #workaround for singleton objects
46        d = self._full_mapping(self.reference)
47        if d is p:
48            p = p1
49        #Indexing
50        for key, value in self.reference.items():
51            self.assertEqual(d[key], value)
52        knownkey = list(self.other.keys())[0]
53        self.assertRaises(KeyError, lambda:d[knownkey])
54        #len
55        self.assertEqual(len(p), 0)
56        self.assertEqual(len(d), len(self.reference))
57        #__contains__
58        for k in self.reference:
59            self.assertIn(k, d)
60        for k in self.other:
61            self.assertNotIn(k, d)
62        #cmp
63        self.assertEqual(p, p)
64        self.assertEqual(d, d)
65        self.assertNotEqual(p, d)
66        self.assertNotEqual(d, p)
67        #bool
68        if p: self.fail("Empty mapping must compare to False")
69        if not d: self.fail("Full mapping must compare to True")
70        # keys(), items(), iterkeys() ...
71        def check_iterandlist(iter, lst, ref):
72            self.assertTrue(hasattr(iter, '__next__'))
73            self.assertTrue(hasattr(iter, '__iter__'))
74            x = list(iter)
75            self.assertTrue(set(x)==set(lst)==set(ref))
76        check_iterandlist(iter(d.keys()), list(d.keys()),
77                          self.reference.keys())
78        check_iterandlist(iter(d), list(d.keys()), self.reference.keys())
79        check_iterandlist(iter(d.values()), list(d.values()),
80                          self.reference.values())
81        check_iterandlist(iter(d.items()), list(d.items()),
82                          self.reference.items())
83        #get
84        key, value = next(iter(d.items()))
85        knownkey, knownvalue = next(iter(self.other.items()))
86        self.assertEqual(d.get(key, knownvalue), value)
87        self.assertEqual(d.get(knownkey, knownvalue), knownvalue)
88        self.assertNotIn(knownkey, d)
89
90    def test_write(self):
91        # Test for write operations on mapping
92        p = self._empty_mapping()
93        #Indexing
94        for key, value in self.reference.items():
95            p[key] = value
96            self.assertEqual(p[key], value)
97        for key in self.reference.keys():
98            del p[key]
99            self.assertRaises(KeyError, lambda:p[key])
100        p = self._empty_mapping()
101        #update
102        p.update(self.reference)
103        self.assertEqual(dict(p), self.reference)
104        items = list(p.items())
105        p = self._empty_mapping()
106        p.update(items)
107        self.assertEqual(dict(p), self.reference)
108        d = self._full_mapping(self.reference)
109        #setdefault
110        key, value = next(iter(d.items()))
111        knownkey, knownvalue = next(iter(self.other.items()))
112        self.assertEqual(d.setdefault(key, knownvalue), value)
113        self.assertEqual(d[key], value)
114        self.assertEqual(d.setdefault(knownkey, knownvalue), knownvalue)
115        self.assertEqual(d[knownkey], knownvalue)
116        #pop
117        self.assertEqual(d.pop(knownkey), knownvalue)
118        self.assertNotIn(knownkey, d)
119        self.assertRaises(KeyError, d.pop, knownkey)
120        default = 909
121        d[knownkey] = knownvalue
122        self.assertEqual(d.pop(knownkey, default), knownvalue)
123        self.assertNotIn(knownkey, d)
124        self.assertEqual(d.pop(knownkey, default), default)
125        #popitem
126        key, value = d.popitem()
127        self.assertNotIn(key, d)
128        self.assertEqual(value, self.reference[key])
129        p=self._empty_mapping()
130        self.assertRaises(KeyError, p.popitem)
131
132    def test_constructor(self):
133        self.assertEqual(self._empty_mapping(), self._empty_mapping())
134
135    def test_bool(self):
136        self.assertTrue(not self._empty_mapping())
137        self.assertTrue(self.reference)
138        self.assertTrue(bool(self._empty_mapping()) is False)
139        self.assertTrue(bool(self.reference) is True)
140
141    def test_keys(self):
142        d = self._empty_mapping()
143        self.assertEqual(list(d.keys()), [])
144        d = self.reference
145        self.assertIn(list(self.inmapping.keys())[0], d.keys())
146        self.assertNotIn(list(self.other.keys())[0], d.keys())
147        self.assertRaises(TypeError, d.keys, None)
148
149    def test_values(self):
150        d = self._empty_mapping()
151        self.assertEqual(list(d.values()), [])
152
153        self.assertRaises(TypeError, d.values, None)
154
155    def test_items(self):
156        d = self._empty_mapping()
157        self.assertEqual(list(d.items()), [])
158
159        self.assertRaises(TypeError, d.items, None)
160
161    def test_len(self):
162        d = self._empty_mapping()
163        self.assertEqual(len(d), 0)
164
165    def test_getitem(self):
166        d = self.reference
167        self.assertEqual(d[list(self.inmapping.keys())[0]],
168                         list(self.inmapping.values())[0])
169
170        self.assertRaises(TypeError, d.__getitem__)
171
172    def test_update(self):
173        # mapping argument
174        d = self._empty_mapping()
175        d.update(self.other)
176        self.assertEqual(list(d.items()), list(self.other.items()))
177
178        # No argument
179        d = self._empty_mapping()
180        d.update()
181        self.assertEqual(d, self._empty_mapping())
182
183        # item sequence
184        d = self._empty_mapping()
185        d.update(self.other.items())
186        self.assertEqual(list(d.items()), list(self.other.items()))
187
188        # Iterator
189        d = self._empty_mapping()
190        d.update(self.other.items())
191        self.assertEqual(list(d.items()), list(self.other.items()))
192
193        # FIXME: Doesn't work with UserDict
194        # self.assertRaises((TypeError, AttributeError), d.update, None)
195        self.assertRaises((TypeError, AttributeError), d.update, 42)
196
197        outerself = self
198        class SimpleUserDict:
199            def __init__(self):
200                self.d = outerself.reference
201            def keys(self):
202                return self.d.keys()
203            def __getitem__(self, i):
204                return self.d[i]
205        d.clear()
206        d.update(SimpleUserDict())
207        i1 = sorted(d.items())
208        i2 = sorted(self.reference.items())
209        self.assertEqual(i1, i2)
210
211        class Exc(Exception): pass
212
213        d = self._empty_mapping()
214        class FailingUserDict:
215            def keys(self):
216                raise Exc
217        self.assertRaises(Exc, d.update, FailingUserDict())
218
219        d.clear()
220
221        class FailingUserDict:
222            def keys(self):
223                class BogonIter:
224                    def __init__(self):
225                        self.i = 1
226                    def __iter__(self):
227                        return self
228                    def __next__(self):
229                        if self.i:
230                            self.i = 0
231                            return 'a'
232                        raise Exc
233                return BogonIter()
234            def __getitem__(self, key):
235                return key
236        self.assertRaises(Exc, d.update, FailingUserDict())
237
238        class FailingUserDict:
239            def keys(self):
240                class BogonIter:
241                    def __init__(self):
242                        self.i = ord('a')
243                    def __iter__(self):
244                        return self
245                    def __next__(self):
246                        if self.i <= ord('z'):
247                            rtn = chr(self.i)
248                            self.i += 1
249                            return rtn
250                        raise StopIteration
251                return BogonIter()
252            def __getitem__(self, key):
253                raise Exc
254        self.assertRaises(Exc, d.update, FailingUserDict())
255
256        d = self._empty_mapping()
257        class badseq(object):
258            def __iter__(self):
259                return self
260            def __next__(self):
261                raise Exc()
262
263        self.assertRaises(Exc, d.update, badseq())
264
265        self.assertRaises(ValueError, d.update, [(1, 2, 3)])
266
267    # no test_fromkeys or test_copy as both os.environ and selves don't support it
268
269    def test_get(self):
270        d = self._empty_mapping()
271        self.assertTrue(d.get(list(self.other.keys())[0]) is None)
272        self.assertEqual(d.get(list(self.other.keys())[0], 3), 3)
273        d = self.reference
274        self.assertTrue(d.get(list(self.other.keys())[0]) is None)
275        self.assertEqual(d.get(list(self.other.keys())[0], 3), 3)
276        self.assertEqual(d.get(list(self.inmapping.keys())[0]),
277                         list(self.inmapping.values())[0])
278        self.assertEqual(d.get(list(self.inmapping.keys())[0], 3),
279                         list(self.inmapping.values())[0])
280        self.assertRaises(TypeError, d.get)
281        self.assertRaises(TypeError, d.get, None, None, None)
282
283    def test_setdefault(self):
284        d = self._empty_mapping()
285        self.assertRaises(TypeError, d.setdefault)
286
287    def test_popitem(self):
288        d = self._empty_mapping()
289        self.assertRaises(KeyError, d.popitem)
290        self.assertRaises(TypeError, d.popitem, 42)
291
292    def test_pop(self):
293        d = self._empty_mapping()
294        k, v = list(self.inmapping.items())[0]
295        d[k] = v
296        self.assertRaises(KeyError, d.pop, list(self.other.keys())[0])
297
298        self.assertEqual(d.pop(k), v)
299        self.assertEqual(len(d), 0)
300
301        self.assertRaises(KeyError, d.pop, k)
302
303
304class TestMappingProtocol(BasicTestMappingProtocol):
305    def test_constructor(self):
306        BasicTestMappingProtocol.test_constructor(self)
307        self.assertTrue(self._empty_mapping() is not self._empty_mapping())
308        self.assertEqual(self.type2test(x=1, y=2), {"x": 1, "y": 2})
309
310    def test_bool(self):
311        BasicTestMappingProtocol.test_bool(self)
312        self.assertTrue(not self._empty_mapping())
313        self.assertTrue(self._full_mapping({"x": "y"}))
314        self.assertTrue(bool(self._empty_mapping()) is False)
315        self.assertTrue(bool(self._full_mapping({"x": "y"})) is True)
316
317    def test_keys(self):
318        BasicTestMappingProtocol.test_keys(self)
319        d = self._empty_mapping()
320        self.assertEqual(list(d.keys()), [])
321        d = self._full_mapping({'a': 1, 'b': 2})
322        k = d.keys()
323        self.assertIn('a', k)
324        self.assertIn('b', k)
325        self.assertNotIn('c', k)
326
327    def test_values(self):
328        BasicTestMappingProtocol.test_values(self)
329        d = self._full_mapping({1:2})
330        self.assertEqual(list(d.values()), [2])
331
332    def test_items(self):
333        BasicTestMappingProtocol.test_items(self)
334
335        d = self._full_mapping({1:2})
336        self.assertEqual(list(d.items()), [(1, 2)])
337
338    def test_contains(self):
339        d = self._empty_mapping()
340        self.assertNotIn('a', d)
341        self.assertTrue(not ('a' in d))
342        self.assertTrue('a' not in d)
343        d = self._full_mapping({'a': 1, 'b': 2})
344        self.assertIn('a', d)
345        self.assertIn('b', d)
346        self.assertNotIn('c', d)
347
348        self.assertRaises(TypeError, d.__contains__)
349
350    def test_len(self):
351        BasicTestMappingProtocol.test_len(self)
352        d = self._full_mapping({'a': 1, 'b': 2})
353        self.assertEqual(len(d), 2)
354
355    def test_getitem(self):
356        BasicTestMappingProtocol.test_getitem(self)
357        d = self._full_mapping({'a': 1, 'b': 2})
358        self.assertEqual(d['a'], 1)
359        self.assertEqual(d['b'], 2)
360        d['c'] = 3
361        d['a'] = 4
362        self.assertEqual(d['c'], 3)
363        self.assertEqual(d['a'], 4)
364        del d['b']
365        self.assertEqual(d, {'a': 4, 'c': 3})
366
367        self.assertRaises(TypeError, d.__getitem__)
368
369    def test_clear(self):
370        d = self._full_mapping({1:1, 2:2, 3:3})
371        d.clear()
372        self.assertEqual(d, {})
373
374        self.assertRaises(TypeError, d.clear, None)
375
376    def test_update(self):
377        BasicTestMappingProtocol.test_update(self)
378        # mapping argument
379        d = self._empty_mapping()
380        d.update({1:100})
381        d.update({2:20})
382        d.update({1:1, 2:2, 3:3})
383        self.assertEqual(d, {1:1, 2:2, 3:3})
384
385        # no argument
386        d.update()
387        self.assertEqual(d, {1:1, 2:2, 3:3})
388
389        # keyword arguments
390        d = self._empty_mapping()
391        d.update(x=100)
392        d.update(y=20)
393        d.update(x=1, y=2, z=3)
394        self.assertEqual(d, {"x":1, "y":2, "z":3})
395
396        # item sequence
397        d = self._empty_mapping()
398        d.update([("x", 100), ("y", 20)])
399        self.assertEqual(d, {"x":100, "y":20})
400
401        # Both item sequence and keyword arguments
402        d = self._empty_mapping()
403        d.update([("x", 100), ("y", 20)], x=1, y=2)
404        self.assertEqual(d, {"x":1, "y":2})
405
406        # iterator
407        d = self._full_mapping({1:3, 2:4})
408        d.update(self._full_mapping({1:2, 3:4, 5:6}).items())
409        self.assertEqual(d, {1:2, 2:4, 3:4, 5:6})
410
411        class SimpleUserDict:
412            def __init__(self):
413                self.d = {1:1, 2:2, 3:3}
414            def keys(self):
415                return self.d.keys()
416            def __getitem__(self, i):
417                return self.d[i]
418        d.clear()
419        d.update(SimpleUserDict())
420        self.assertEqual(d, {1:1, 2:2, 3:3})
421
422    def test_fromkeys(self):
423        self.assertEqual(self.type2test.fromkeys('abc'), {'a':None, 'b':None, 'c':None})
424        d = self._empty_mapping()
425        self.assertTrue(not(d.fromkeys('abc') is d))
426        self.assertEqual(d.fromkeys('abc'), {'a':None, 'b':None, 'c':None})
427        self.assertEqual(d.fromkeys((4,5),0), {4:0, 5:0})
428        self.assertEqual(d.fromkeys([]), {})
429        def g():
430            yield 1
431        self.assertEqual(d.fromkeys(g()), {1:None})
432        self.assertRaises(TypeError, {}.fromkeys, 3)
433        class dictlike(self.type2test): pass
434        self.assertEqual(dictlike.fromkeys('a'), {'a':None})
435        self.assertEqual(dictlike().fromkeys('a'), {'a':None})
436        self.assertTrue(dictlike.fromkeys('a').__class__ is dictlike)
437        self.assertTrue(dictlike().fromkeys('a').__class__ is dictlike)
438        self.assertTrue(type(dictlike.fromkeys('a')) is dictlike)
439        class mydict(self.type2test):
440            def __new__(cls):
441                return collections.UserDict()
442        ud = mydict.fromkeys('ab')
443        self.assertEqual(ud, {'a':None, 'b':None})
444        self.assertIsInstance(ud, collections.UserDict)
445        self.assertRaises(TypeError, dict.fromkeys)
446
447        class Exc(Exception): pass
448
449        class baddict1(self.type2test):
450            def __init__(self):
451                raise Exc()
452
453        self.assertRaises(Exc, baddict1.fromkeys, [1])
454
455        class BadSeq(object):
456            def __iter__(self):
457                return self
458            def __next__(self):
459                raise Exc()
460
461        self.assertRaises(Exc, self.type2test.fromkeys, BadSeq())
462
463        class baddict2(self.type2test):
464            def __setitem__(self, key, value):
465                raise Exc()
466
467        self.assertRaises(Exc, baddict2.fromkeys, [1])
468
469    def test_copy(self):
470        d = self._full_mapping({1:1, 2:2, 3:3})
471        self.assertEqual(d.copy(), {1:1, 2:2, 3:3})
472        d = self._empty_mapping()
473        self.assertEqual(d.copy(), d)
474        self.assertIsInstance(d.copy(), d.__class__)
475        self.assertRaises(TypeError, d.copy, None)
476
477    def test_get(self):
478        BasicTestMappingProtocol.test_get(self)
479        d = self._empty_mapping()
480        self.assertTrue(d.get('c') is None)
481        self.assertEqual(d.get('c', 3), 3)
482        d = self._full_mapping({'a' : 1, 'b' : 2})
483        self.assertTrue(d.get('c') is None)
484        self.assertEqual(d.get('c', 3), 3)
485        self.assertEqual(d.get('a'), 1)
486        self.assertEqual(d.get('a', 3), 1)
487
488    def test_setdefault(self):
489        BasicTestMappingProtocol.test_setdefault(self)
490        d = self._empty_mapping()
491        self.assertTrue(d.setdefault('key0') is None)
492        d.setdefault('key0', [])
493        self.assertTrue(d.setdefault('key0') is None)
494        d.setdefault('key', []).append(3)
495        self.assertEqual(d['key'][0], 3)
496        d.setdefault('key', []).append(4)
497        self.assertEqual(len(d['key']), 2)
498
499    def test_popitem(self):
500        BasicTestMappingProtocol.test_popitem(self)
501        for copymode in -1, +1:
502            # -1: b has same structure as a
503            # +1: b is a.copy()
504            for log2size in range(12):
505                size = 2**log2size
506                a = self._empty_mapping()
507                b = self._empty_mapping()
508                for i in range(size):
509                    a[repr(i)] = i
510                    if copymode < 0:
511                        b[repr(i)] = i
512                if copymode > 0:
513                    b = a.copy()
514                for i in range(size):
515                    ka, va = ta = a.popitem()
516                    self.assertEqual(va, int(ka))
517                    kb, vb = tb = b.popitem()
518                    self.assertEqual(vb, int(kb))
519                    self.assertTrue(not(copymode < 0 and ta != tb))
520                self.assertTrue(not a)
521                self.assertTrue(not b)
522
523    def test_pop(self):
524        BasicTestMappingProtocol.test_pop(self)
525
526        # Tests for pop with specified key
527        d = self._empty_mapping()
528        k, v = 'abc', 'def'
529
530        self.assertEqual(d.pop(k, v), v)
531        d[k] = v
532        self.assertEqual(d.pop(k, 1), v)
533
534
535class TestHashMappingProtocol(TestMappingProtocol):
536
537    def test_getitem(self):
538        TestMappingProtocol.test_getitem(self)
539        class Exc(Exception): pass
540
541        class BadEq(object):
542            def __eq__(self, other):
543                raise Exc()
544            def __hash__(self):
545                return 24
546
547        d = self._empty_mapping()
548        d[BadEq()] = 42
549        self.assertRaises(KeyError, d.__getitem__, 23)
550
551        class BadHash(object):
552            fail = False
553            def __hash__(self):
554                if self.fail:
555                    raise Exc()
556                else:
557                    return 42
558
559        d = self._empty_mapping()
560        x = BadHash()
561        d[x] = 42
562        x.fail = True
563        self.assertRaises(Exc, d.__getitem__, x)
564
565    def test_fromkeys(self):
566        TestMappingProtocol.test_fromkeys(self)
567        class mydict(self.type2test):
568            def __new__(cls):
569                return collections.UserDict()
570        ud = mydict.fromkeys('ab')
571        self.assertEqual(ud, {'a':None, 'b':None})
572        self.assertIsInstance(ud, collections.UserDict)
573
574    def test_pop(self):
575        TestMappingProtocol.test_pop(self)
576
577        class Exc(Exception): pass
578
579        class BadHash(object):
580            fail = False
581            def __hash__(self):
582                if self.fail:
583                    raise Exc()
584                else:
585                    return 42
586
587        d = self._empty_mapping()
588        x = BadHash()
589        d[x] = 42
590        x.fail = True
591        self.assertRaises(Exc, d.pop, x)
592
593    def test_mutatingiteration(self):
594        d = self._empty_mapping()
595        d[1] = 1
596        try:
597            for i in d:
598                d[i+1] = 1
599        except RuntimeError:
600            pass
601        else:
602            self.fail("changing dict size during iteration doesn't raise Error")
603
604    def test_repr(self):
605        d = self._empty_mapping()
606        self.assertEqual(repr(d), '{}')
607        d[1] = 2
608        self.assertEqual(repr(d), '{1: 2}')
609        d = self._empty_mapping()
610        d[1] = d
611        self.assertEqual(repr(d), '{1: {...}}')
612
613        class Exc(Exception): pass
614
615        class BadRepr(object):
616            def __repr__(self):
617                raise Exc()
618
619        d = self._full_mapping({1: BadRepr()})
620        self.assertRaises(Exc, repr, d)
621
622    def test_eq(self):
623        self.assertEqual(self._empty_mapping(), self._empty_mapping())
624        self.assertEqual(self._full_mapping({1: 2}),
625                         self._full_mapping({1: 2}))
626
627        class Exc(Exception): pass
628
629        class BadCmp(object):
630            def __eq__(self, other):
631                raise Exc()
632            def __hash__(self):
633                return 1
634
635        d1 = self._full_mapping({BadCmp(): 1})
636        d2 = self._full_mapping({1: 1})
637        self.assertRaises(Exc, lambda: BadCmp()==1)
638        self.assertRaises(Exc, lambda: d1==d2)
639
640    def test_setdefault(self):
641        TestMappingProtocol.test_setdefault(self)
642
643        class Exc(Exception): pass
644
645        class BadHash(object):
646            fail = False
647            def __hash__(self):
648                if self.fail:
649                    raise Exc()
650                else:
651                    return 42
652
653        d = self._empty_mapping()
654        x = BadHash()
655        d[x] = 42
656        x.fail = True
657        self.assertRaises(Exc, d.setdefault, x, [])
658