File: test_orderedmultidict.py

package info (click to toggle)
python-orderedmultidict 1.0.1-1.1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 308 kB
  • sloc: python: 1,305; makefile: 2
file content (925 lines) | stat: -rw-r--r-- 36,294 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
# -*- coding: utf-8 -*-

#
# omdict - Ordered Multivalue Dictionary.
#
# Ansgar Grunseid
# grunseid.com
# grunseid@gmail.com
#
# License: Build Amazing Things (Unlicense)
#

from __future__ import absolute_import

import unittest
from itertools import product, repeat

from itertools import zip_longest

from orderedmultidict.orderedmultidict import omdict

try:
    from collections import OrderedDict as odict  # Python 2.7+.
except ImportError:
    from ordereddict import OrderedDict as odict  # Python 2.4-2.6.

_unique = object()


def callable_attr(o, attr):
    return hasattr(o, attr) and callable(getattr(o, attr))


def is_iterator(i):
    return callable_attr(i, 'next') or callable_attr(i, '__next__')


# Utility list subclass to expose items() and iteritems() methods on basic
# lists. This provides a common iteration interface for lists and dictionaries
# for looping through their items without having to test for and maintain two
# separate bodies, one for lists and one for dictionaries.
#
# So instead of requiring two bodies, one for lists and one for dicts
#
#  lists = [[(1,1),(2,2)]]
#  dicts = [{1:1,2:2}]
#  for lst in lists:
#    lst == ...
#  for dic in dicts:
#    dic.items() == ...
#
# list and dictionary looping bodies can be merged with itemlist
#
#  itemlist = [itemlist([(1,1),(2,2)]), {1:1,2:2}]
#  for ilist in itemlist:
#    ilist.items() == ...
#


class itemlist(list):

    def items(self):
        return self

    def iteritems(self):
        return iter(self)


class TestOmdict(unittest.TestCase):

    def setUp(self):
        self.inits = [
            {}, {1: 1}, {1: 1, 2: 2, 3: 3}, {None: None}, {
                None: None, 1: 1, 2: 2}, {False: False},
        ]
        self.inits += list(map(itemlist, [
            [], [(1, 1)], [(1, 1), (2, 2)], [(1, 1), (2, 2), (1, 1)],
            [(1, 1), (1, 1), (1, 1)], [(None, None), (None, None)],
            [(False, False)],
            [(None, 1), (1, None), (None, None), (None, 1), (1, None)],
        ]))

        # Updates to test update() and updateall().
        self.updates = [
            {}, {7: 7}, {7: 7, 8: 8, 9: 9}, {None: None}, {1: 1, 2: 2}]
        self.updates += list(map(itemlist, [
            [], [(7, 7)], [(7, 7), (8, 8), (9, 9)], [(None, 'none')],
            [(9, 9), (1, 2)], [(7, 7), (7, 7), (8, 8), (7, 77)],
            [(1, 11), (1, 111), (1, 1111), (2, 22),
                (2, 222), ('a', 'a'), ('a', 'aa')],
        ]))

        self.keyword_updates = [
            {}, {'1': 1}, {'1': 1, '2': 2}, {
                'sup': 'pumps', 'scewps': None}, {'aa': 'aa'},
        ]

        # Items not initially in any of the multidict inputs self.inits.
        self.nonitems = [
            (44, 44), (None, 44), (55, None), ('a', 'b'), (11, 11), (22, 22)]

        # Keys not initially in any of the multidict inputs self.inits or in
        # self.nonitems.
        self.nonkeys = [_unique, 'asdfasdosduf', 'oaisfiapsn', 'ioausopdaui']

        self.valuelist = [1, 2, 3, None, 'a', 'b', object()]

    def test_init(self):
        for init in self.inits:
            omd = omdict(init)
            assert omd.allitems() == list(init.items())

            omd1 = omdict(init)
            omd2 = omdict(omd1)
            assert omd1.allitems() == list(omd2.allitems())

        # Support both *args and **kwargs dictionary initialization.
        items = [('sape', 4139), ('guido', 4127), ('jack', 4098)]
        assert omdict(items).allitems() == items
        omd_item_set = set(omdict(sape=4139, guido=4127, jack=4098).items())
        assert omd_item_set == set(items)  # Keyword order isn't preserved.

    def test_load(self):
        omd = omdict()
        for init in self.inits:
            assert omd.load(init) == omd
            assert omd.allitems() == list(init.items())

    def test_copy(self):
        for init in self.inits:
            omd = omdict(init)
            copy = omd.copy()
            assert omd is not copy and omd == copy

    def test_clear(self):
        for init in self.inits:
            omd = omdict(init)
            omd.clear()
            assert omd.items() == []

    def test_fromkeys(self):
        for init in self.inits:
            keys = [key for key, value in init.items()]
            allitems = omdict.fromkeys(keys, _unique).allitems()
            assert allitems == list(zip(keys, repeat(_unique)))

    def test_has_key(self):
        for init in self.inits:
            omd = omdict(init)
            for key, value in init.items():
                assert key in omd

    def test_update(self):
        # Some manual tests.
        omd = omdict()
        omd.update([(1, 1), (1, 11), (2, 2), (3, 3), (1, 111), (2, 22)])
        assert omd.allitems() == [(1, 111), (2, 22), (3, 3)]

        omd = omdict([(1, 1), (1, 11), (2, 2), (3, 3), (1, 111), (2, 22)])
        omd.update({1: None, 2: None, 3: None})
        assert omd.allitems() == [(1, None), (2, None), (3, None)]

        for init in self.inits:
            zipped = zip(self.updates, self.keyword_updates)
            for update, keyword_update in zipped:
                omd1, omd2, omd3 = omdict(init), omdict(init), omdict(init)
                oldomd = omd1.copy()
                # Reduce the update to just the final items that will be
                # present post update(), where repeated keys will be reduced to
                # their last occurring value. For example, [(7,7),(7,8)] would
                # be reduced to [(7,8)].
                reduced = [
                    i for i in update.items() if i in odict(update).items()]

                # Update with a dictionary.
                omd1.update(update)
                # Update with keyword expansion.
                omd2.update(**keyword_update)
                # Update with both a dictionary and keyword expansion.
                omd3.update(update, **keyword_update)

                # Verification.
                if update or keyword_update:
                    for key, value in reduced:
                        assert key in omd1 and key in omd3
                    for key, value in keyword_update.items():
                        assert key in omd2 and key in omd3
                else:
                    assert omd1 == omd2 == omd3 == oldomd

    def test_updateall(self):
        # Some manual tests.
        omd = omdict([(1, 1), (1, 11), (2, 2), (3, 3), (1, 111), (2, 22)])
        omd.updateall({1: None, 2: None, 3: None})
        assert omd.allitems() == [(1, None), (2, None), (3, None)]

        omd = omdict([(1, 1), (1, 11), (2, 2), (3, 3), (1, 111), (2, 22)])
        omd.updateall([(1, None), (2, None), (3, None), (1, None), (2, None)])
        assert omd.allitems() == [
            (1, None), (1, None), (2, None), (3, None), (2, None)]

        omd = omdict([(1, 1), (1, 11), (2, 2), (3, 3), (1, 111), (2, 22)])
        omd.updateall([(1, None), (1, None), (1, None), (2, None)])
        assert omd.allitems() == [
            (1, None), (1, None), (2, None), (3, 3), (1, None)]

        for init in self.inits:
            zipped = zip(self.updates, self.keyword_updates)
            for update, keyword_update in zipped:
                omd1, omd2, omd3 = omdict(init), omdict(init), omdict(init)
                oldomd = omd1.copy()

                # Update with a dictionary.
                omd1.updateall(update)
                # Update with keyword expansion.
                omd2.updateall(**keyword_update)
                # Update with both a dictionary and keyword expansion.
                omd3.updateall(update, **keyword_update)

                # Verification.
                if update or keyword_update:
                    for key, value in update.items():
                        assert key in omd1 and key in omd3
                        assert value in omd1.getlist(
                            key) and value in omd3.getlist(key)
                    for key, value in keyword_update.items():
                        assert key in omd2 and key in omd3
                        assert omd2.getlist(
                            key) == omd3.getlist(key) == [value]
                else:
                    assert omd1 == omd2 == oldomd

    def test_get(self):
        for init in self.inits:
            omd = omdict(init)
            for key in omd.iterkeys():
                assert omd.get(key) == omd[key]
            for nonkey in self.nonkeys:
                assert omd.get(nonkey) is None
                assert omd.get(nonkey, _unique) == _unique

    def test_getlist(self):
        for init in self.inits:
            omd = omdict(init)
            for key in omd:
                assert omd.getlist(
                    key) == [v for k, v in omd.allitems() if k == key]
            for nonkey in self.nonkeys:
                assert omd.getlist(nonkey) == []
                assert omd.getlist(nonkey, _unique) == _unique

    def test_setdefault(self):
        for init in self.inits:
            omd = omdict(init)
            for key in omd.iterkeys():
                assert omd.setdefault(key, _unique) == omd[key]
            for nonkey in self.nonkeys:
                assert omd.setdefault(nonkey) is None
                assert omd[nonkey] is None
            omd.load(init)
            for nonkey in self.nonkeys:
                assert omd.setdefault(nonkey, 123456) == 123456
                assert omd[nonkey] == 123456

    def test_setdefaultlist(self):
        for init in self.inits:
            omd = omdict(init)
            for key in omd.iterkeys():
                assert omd.setdefaultlist(key, _unique) == omd.getlist(key)
            for nonkey in self.nonkeys:
                assert omd.setdefaultlist(nonkey) == [None]
                assert omd.getlist(nonkey) == [None]
            omd.load(init)
            for nonkey in self.nonkeys:
                assert omd.setdefaultlist(nonkey, [1, 2, 3]) == [1, 2, 3]
                assert omd.getlist(nonkey) == [1, 2, 3]

        # setdefaultlist() with an empty list of values does nothing.
        for init in self.inits:
            omd = omdict(init)
            for key in omd.iterkeys():
                values = omd.getlist(key)
                assert key in omd
                assert omd.setdefaultlist(key, []) == values
                assert key in omd and omd.getlist(key) == values
            for nonkey in self.nonkeys:
                assert nonkey not in omd
                assert omd.setdefaultlist(nonkey, []) == []
                assert nonkey not in omd

    def test_add(self):
        for init in self.inits:
            omd = omdict(init)
            for key, value in self.nonitems:
                assert (key, value) not in omd.allitems()
                assert omd.add(key, value) == omd
                assert omd.getlist(key)[-1] == value
                assert omd.allitems()[-1] == (key, value)

            # Repeat the add() calls with the same items and make sure the old
            # items aren't replaced.
            oldomd = omd.copy()
            for key, value in self.nonitems:
                assert (key, value) in omd.allitems()
                assert omd.add(key, value) == omd
                assert len(omd.getlist(key)) == len(oldomd.getlist(key)) + 1
                assert omd.getlist(key)[-1] == value
                assert omd.allitems()[-1] == (key, value)

            # Assert that containers are valid values, too, not just immutables
            # like integers.
            assert omd.add(_unique, self.updates) == omd
            assert omd.getlist(_unique)[-1] == self.updates
            assert omd.allitems()[-1] == (_unique, self.updates)

            # Add() doesn't require a value, and when one isn't provided it
            # defaults to None.
            omd = omdict(init)
            assert omd.add(_unique) == omd
            assert _unique in omd and omd[_unique] is None

    def test_addlist(self):
        for init in self.inits:
            omd = omdict(init)
            for nonkey in self.nonkeys:
                assert (nonkey, self.valuelist) not in omd.allitems()
                assert omd.addlist(nonkey, self.valuelist) == omd
                assert omd.getlist(nonkey) == self.valuelist
                assert (omd.allitems()[-1 * len(self.valuelist):] ==
                        list(zip(repeat(nonkey), self.valuelist)))

            # Repeat the addlist() calls with the same items and make sure the
            # old items aren't replaced.
            oldomd = omd.copy()
            for nonkey in self.nonkeys:
                for value in self.valuelist:
                    assert (nonkey, value) in omd.allitems()
                assert omd.addlist(nonkey, self.valuelist) == omd
                assert len(omd.getlist(nonkey)) == (
                    len(oldomd.getlist(nonkey)) + len(self.valuelist))
                assert omd.getlist(nonkey) == oldomd.getlist(
                    nonkey) + self.valuelist
                assert (omd.allitems()[-1 * len(self.valuelist):] ==
                        list(zip(repeat(nonkey), self.valuelist)))

            # If an empty list is provided to addlist(), nothing is added.
            omd = omdict(init)
            for nonkey in self.nonkeys:
                assert omd.addlist(nonkey) == omd and nonkey not in omd
                assert omd.addlist(nonkey, []) == omd and nonkey not in omd

    def test_setlist(self):
        for init in self.inits:
            omd = omdict(init)
            for key in (omd.keys() + self.nonkeys):
                if key in omd:
                    assert omd.getlist(key) != self.valuelist
                assert omd.setlist(key, self.valuelist)
                assert key in omd and omd.getlist(key) == self.valuelist

        # Setting a key to an empty list is identical to deleting the key.
        for init in self.inits:
            omd = omdict(init)
            for nonkey in self.nonkeys:
                assert nonkey not in omd
                omd.setlist(nonkey, [])
                assert nonkey not in omd
            for key in omd.keys():
                assert key in omd
                omd.setlist(key, [])
                assert key not in omd
            assert not omd

    def test_removevalues(self):
        for init in self.inits:
            omd = omdict(init)
            removevals = omd.removevalues  # Shorten to linewrap for PEP 8.
            for nonkey in self.nonkeys:
                obj = object()
                values = [1, 1.1, '1.1', (), [], {}, obj, 5.5, '1.1']

                assert removevals(nonkey, []).getlist(nonkey) == []
                assert removevals(nonkey, values).getlist(nonkey) == []

                omd.addlist(nonkey, values).removevalues(nonkey, [])
                assert omd.getlist(nonkey) == values
                assert removevals(nonkey, values).getlist(nonkey) == []

                omd.addlist(nonkey, values)
                assert (removevals(nonkey, [1]).getlist(nonkey) ==
                        [1.1, '1.1', (), [], {}, obj, 5.5, '1.1'])
                assert (removevals(nonkey, ['1.1', obj]).getlist(nonkey) ==
                        [1.1, (), [], {}, 5.5])
                assert (removevals(nonkey, [[], 5.5, ()]).getlist(nonkey) ==
                        [1.1, {}])
                assert removevals(nonkey, [{}]).getlist(nonkey) == [1.1]
                assert removevals(nonkey, [1.1]).getlist(nonkey) == []
                assert removevals(
                    nonkey, [9, 9.9, 'nope']).getlist(nonkey) == []

    def test_pop(self):
        self._test_pop_poplist(lambda omd, key: omd.get(key) == omd.pop(key))

    def test_poplist(self):
        self._test_pop_poplist(
            lambda omd, key: omd.getlist(key) == omd.poplist(key))

    def _test_pop_poplist(self, assert_lambda):
        for init in self.inits:
            omd = omdict(init)
            items = omd.items()
            for key in list(omd.keys()):
                assert assert_lambda(omd, key)
                newitems = [item for item in items if item[0] != key]
                assert omd.items() == newitems
                items = newitems

            omd.load(init)
            for nonkey in self.nonkeys:
                self.assertRaises(KeyError, omd.pop, nonkey)
                assert omd.pop(nonkey, _unique) == _unique
                self.assertRaises(KeyError, omd.poplist, nonkey)
                assert omd.poplist(nonkey, _unique) == _unique

    def test_popvalue(self):
        # popvalue() with no value provided.
        for init in self.inits:
            for last in [True, False]:
                omd = omdict(init)
                allitems = omd.allitems()
                while omd.keys():
                    for key in omd.keys():
                        if last:
                            value = omd.getlist(key)[-1]
                            _rremove(allitems, (key, value))
                        else:
                            value = omd[key]
                            allitems.remove((key, value))

                        assert value == omd.popvalue(key, last=last)
                        assert omd.allitems() == allitems

            omd.load(init)
            for nonkey in self.nonkeys:
                self.assertRaises(KeyError, omd.popvalue, nonkey)
                assert omd.popvalue(nonkey, default=_unique) == _unique

        # popvalue() with value provided.
        #   last = True (default).
        omd = omdict([(1, 1), (2, 2), (3, 3), (2, 2), (3, 3), (2, 2)])
        assert omd.popvalue(2, 2) == 2
        assert omd.allitems() == [(1, 1), (2, 2), (3, 3), (2, 2), (3, 3)]
        assert omd.popvalue(2, 2) == 2
        assert omd.allitems() == [(1, 1), (2, 2), (3, 3), (3, 3)]
        assert omd.popvalue(2, 2) == 2
        assert omd.allitems() == [(1, 1), (3, 3), (3, 3)]
        #   last = False.
        omd = omdict([(3, 3), (2, 2), (3, 3), (2, 2), (3, 3), (2, 2)])
        assert omd.popvalue(2, 2, last=True) == 2
        assert omd.allitems() == [(3, 3), (2, 2), (3, 3), (2, 2), (3, 3)]
        assert omd.popvalue(2, 2, last=True) == 2
        assert omd.allitems() == [(3, 3), (2, 2), (3, 3), (3, 3)]
        assert omd.popvalue(2, 2, last=True) == 2
        assert omd.allitems() == [(3, 3), (3, 3), (3, 3)]

        # Invalid key.
        self.assertRaises(KeyError, omd.popvalue, _unique, _unique)
        self.assertRaises(KeyError, omd.popvalue, _unique, 2)
        self.assertRaises(KeyError, omd.popvalue, _unique, 22)
        self.assertRaises(KeyError, omd.popvalue, _unique, _unique, last=False)
        self.assertRaises(KeyError, omd.popvalue, _unique, 2)
        self.assertRaises(KeyError, omd.popvalue, _unique, 22)
        assert omd.popvalue(_unique, _unique, 'sup') == 'sup'
        assert omd.popvalue(_unique, 2, 'sup') == 'sup'
        assert omd.popvalue(_unique, 22, 'sup') == 'sup'

        # Valid key, invalid value.
        self.assertRaises(ValueError, omd.popvalue, 3, _unique)
        self.assertRaises(ValueError, omd.popvalue, 3, _unique, False)

    def test_popitem(self):
        for init in self.inits:
            # All permutations of booleans <fromall> and <last>.
            for fromall, last in product([True, False], repeat=2):
                omd = omdict(init)
                allitems = omd.allitems()
                while omd.allitems():
                    if fromall:
                        key, value = omd.allitems()[-1 if last else 0]
                    else:
                        key = omd.keys()[-1 if last else 0]
                        value = omd[key]

                    popkey, popvalue = omd.popitem(fromall=fromall, last=last)
                    assert popkey == key and popvalue == value

                    if fromall:
                        if last:
                            _rremove(allitems, (key, value))
                        else:
                            allitems.remove((key, value))
                    else:
                        allitems = [(k, v) for k, v in allitems if k != key]
                    assert omd.allitems() == allitems

            omd = omdict()
            self.assertRaises(KeyError, omd.popitem)

    def test_poplistitem(self):
        for init in self.inits:
            for last in [True, False]:
                omd, omdcopy = omdict(init), omdict(init)
                while omd.keys():
                    key, valuelist = omd.poplistitem(last=last)
                    assert key == omdcopy.keys()[-1 if last else 0]
                    assert valuelist == omdcopy.getlist(key)
                    omdcopy.pop(omdcopy.keys()[-1 if last else 0])

                # poplistitem() on an empty omdict.
                self.assertRaises(KeyError, omd.poplistitem)

    # Tests every non-'all' items, keys, values, lists method: items(), keys(),
    # values(), lists(), listitems() and their iterators iteritems(),
    # iterkeys(), itervalues(), iterlists(), and iterlistitems().
    def test_nonall_item_key_value_lists(self):
        for init in self.inits:
            dic = odict(init.items())
            omd = omdict(init.items())

            # Testing items(), keys(), values(), lists(), and listitems().
            assert omd.items() == list(dic.items())
            assert omd.keys() == list(dic.keys())
            assert omd.values() == list(dic.values())
            iterator = zip(omd.keys(), omd.lists(), omd.listitems())
            for key, valuelist, listitem in iterator:
                assert omd.values(key) == omd.getlist(key) == valuelist
                assert omd.items(
                    key) == [i for i in init.items() if i[0] == key]
                assert listitem == (key, valuelist)

            # Testing iteritems(), iterkeys(), itervalues(), and iterlists().
            assert is_iterator(omd.iterkeys())
            for key1, key2 in zip(omd.iterkeys(), dic.keys()):
                assert key1 == key2
            assert is_iterator(omd.itervalues())
            for val1, val2 in zip(omd.itervalues(), dic.values()):
                assert val1 == val2
            assert is_iterator(omd.iteritems())
            for item1, item2 in zip(omd.iteritems(), dic.items()):
                assert item1 == item2
            assert is_iterator(omd.iterlists())
            for key, values in zip(omd.keys(), omd.iterlists()):
                assert omd.getlist(key) == values
            assert is_iterator(omd.iterlistitems())
            iterator = zip(
                omd.iterkeys(), omd.iterlists(), omd.iterlistitems())
            for key, valuelist, listitem in iterator:
                assert listitem == (key, valuelist)

            # Test iteritems() and itervalues() with a key.
            for key in omd.iterkeys():
                assert is_iterator(omd.iteritems(key))
                assert list(omd.iteritems(key)) == list(zip(
                    repeat(key), omd.getlist(key)))
                assert is_iterator(omd.itervalues(key))
                assert list(omd.itervalues(key)) == omd.getlist(key)
            for nonkey in self.nonkeys:
                self.assertRaises(KeyError, omd.iteritems, nonkey)
                self.assertRaises(KeyError, omd.itervalues, nonkey)

    # Tests every 'all' items, keys, values method: allitems(), allkeys(),
    # allvalues() and their iterators iterallitems(), iterallkeys(),
    # iterallvalues().
    def test_all_items_keys_values_iterall_items_keys_values(self):
        for init in self.inits:
            omd = omdict(init)
            # map(list, zip(*lst)) - doesn't work if lst is empty, lst == [].
            keys = [key for key, value in init.items()]
            values = [value for key, value in init.items()]

            # Test allitems(), allkeys(), allvalues().
            assert omd.allitems() == list(init.items())
            assert omd.allkeys() == keys
            assert omd.allvalues() == values

            # Test iterallitems(), iterallkeys(), iterallvalues().
            for key1, key2 in zip(omd.iterallkeys(), keys):
                assert key1 == key2
            for val1, val2 in zip(omd.iterallvalues(), values):
                assert val1 == val2
            for item1, item2 in zip(omd.iterallitems(), init.items()):
                assert item1 == item2

            # Test allitems(), allvalues(), iterallitems() and iterallvalues()
            # with a key.
            for key in omd.iterkeys():
                assert (omd.allvalues(key) == list(omd.iterallvalues(key)) ==
                        omd.getlist(key))
                assert (omd.allitems(key) == list(omd.iterallitems(key)) ==
                        list(zip(repeat(key), omd.getlist(key))))
            for nonkey in self.nonkeys:
                self.assertRaises(KeyError, omd.allvalues, nonkey)
                self.assertRaises(KeyError, omd.allitems, nonkey)
                self.assertRaises(KeyError, omd.iterallvalues, nonkey)
                self.assertRaises(KeyError, omd.iterallitems, nonkey)

    def test_reverse(self):
        for init in self.inits:
            reversed = list(init.items())[::-1]
            assert omdict(init).reverse().allitems() == reversed

    def test_eq(self):
        for init in self.inits:
            d, omd = dict(init), omdict(init)
            assert d == omd
            assert omd == omd
            assert omd == omd.copy()

    def test_ne(self):
        diff = omdict([(_unique, _unique)])
        for init in self.inits:
            assert omdict(init) != diff
            # Compare to basic types.
            for basic in [1, 1.1, '1.1', (), [], object()]:
                assert omdict(init) != basic

    def test_len(self):
        for init in self.inits:
            assert len(omdict(init)) == len(dict(init))

    def test_size(self):
        for init in self.inits:
            assert omdict(init).size() == len(init)

    def test_iter(self):
        for init in self.inits:
            omd = omdict(init)
            for key1, key2 in zip_longest(iter(omd), omd.iterkeys()):
                assert key1 == key2

    def test_contains(self):
        for init in self.inits:
            omd = omdict(init)
            for key, value in init.items():
                assert key in omd

    def test_getitem(self):
        for init in self.inits:
            dic = dict(init)
            omd = omdict(init)
            for key in omd.iterkeys():
                assert omd[key] == dic[key]

        omd = omdict()
        self.assertRaises(KeyError, omd.__getitem__, _unique)

    def test_set_setitem(self):
        for init in self.inits:
            omd = omdict()
            omd2 = omdict()
            for key, value in init.items():
                omd[key] = value
                assert omd2.set(key, value) == omd2
                assert omd == omd2 and omd[key] == value

            # Store containers as values, not just immutables like integers.
            omd[_unique] = self.valuelist
            assert omd2.set(_unique, self.valuelist) == omd2
            assert omd == omd2 and omd[_unique] == self.valuelist

    def test_delitem(self):
        for init in self.inits:
            omd = omdict(init)
            for key in list(omd.keys()):
                assert key in omd
                del omd[key]
                assert key not in omd

    def test_nonzero(self):
        for init in self.inits:
            if init:
                assert omdict(init)
            else:
                assert not omdict(init)

    def test_str(self):
        for init in self.inits:
            omd = omdict(init)
            s = '{%s}' % ', '.join(
                map(lambda p: '%s: %s' % (p[0], p[1]), omd.allitems()))
            assert s == str(omd)

    def test_odict_omdict_parity(self):
        for init in self.inits:
            d = odict(init)
            omd = omdict(init)

            self._compare_odict_and_omddict(d, omd)
            self._compare_odict_and_omddict(d.copy(), omd.copy())  # copy().
            d.clear(), omd.clear()  # clear().
            self._compare_odict_and_omddict(d, omd)

            assert dict().update(init) == omdict().update(init)  # update().
            dict_fromkeys = list(d.fromkeys(init).items())
            omdict_fromkeys = list(omd.fromkeys(init).items())
            assert dict_fromkeys == omdict_fromkeys  # fromkeys()

    def _compare_odict_and_omddict(self, d, omd):
        assert len(d) == len(omd)  # __len__().

        # __contains__(), has_key(), get(), and setdefault().
        for dkey, omdkey in zip(d, omd):
            assert dkey == omdkey and dkey in d and omdkey in omd
            assert dkey in d and omdkey in omd
            assert d.get(dkey) == omd.get(omdkey)
            d.setdefault(dkey, _unique)
            omd.setdefault(omdkey, _unique)
            assert d.get(dkey) == omd.get(omdkey) and d.get(dkey) != _unique
        for nonkey in self.nonkeys:
            assert d.get(nonkey) == omd.get(nonkey) is None
            d.setdefault(nonkey, _unique)
            omd.setdefault(nonkey, _unique)
            assert d.get(nonkey) == omd.get(nonkey) == _unique

        # items(), keys, values(), iteritems(), iterkeys, and itervalues().
        iterators = [
            zip(d.items(), omd.items(), d.keys(), omd.keys(),
                d.values(), omd.values()),
            zip(d.items(), omd.items(), d.keys(),
                omd.keys(), d.values(), omd.values())]
        for iterator in iterators:
            for ditem, omditem, dkey, omdkey, dvalue, omdvalue in iterator:
                assert dkey == omdkey
                assert ditem == omditem
                assert dvalue == omdvalue

        # pop().
        dcopy, omdcopy = d.copy(), omd.copy()
        while dcopy and omdcopy:
            dpop = dcopy.pop(list(dcopy.keys())[0])
            omdpop = omdcopy.pop(list(omdcopy.keys())[0])
            assert dpop == omdpop
        # popitem().
        dcopy, omdcopy = d.copy(), omd.copy()
        while dcopy and omdcopy:
            assert dcopy.popitem() == omdcopy.popitem()

        # __getitem__().
        for dkey, omdkey in zip(d.keys(), omd.keys()):
            assert d[dkey] == omd[omdkey]
        # __setitem__().
        for dkey, omdkey in zip(d, omd):
            d[dkey] = _unique
            omd[omdkey] = _unique
            assert dkey == omdkey and d[dkey] == omd[omdkey]
        # __delitem__().
        while d and omd:
            dkey, omdkey = list(d.keys())[0], list(omd.keys())[0]
            del d[dkey]
            del omd[omdkey]
            assert dkey == omdkey and dkey not in d and omdkey not in omd

    def test_fundamentals(self):
        # Gets, sets, and pops.
        omd = omdict()
        omd[1] = 1
        omd[2] = 2
        assert omd.allitems() == [(1, 1), (2, 2)]
        omd[1] = 11
        assert omd.allitems() == [(1, 11), (2, 2)]
        omd.add(1, 1.1)
        assert omd.allitems() == [(1, 11), (2, 2), (1, 1.1)]
        assert omd.popvalue(1) == 1.1
        assert omd.allitems() == [(1, 11), (2, 2)]
        omd.popvalue(2)
        assert omd.allitems() == [(1, 11)]
        omd[2] = [2, 2]
        assert omd.allitems() == [(1, 11), (2, [2, 2])]
        omd[1] = None
        assert omd.allitems() == [(1, None), (2, [2, 2])]
        omd.add(2, None)
        assert omd.allitems() == [(1, None), (2, [2, 2]), (2, None)]
        del omd[2]
        assert omd.allitems() == [(1, None)]
        omd[3] = 3
        assert omd.allitems() == [(1, None), (3, 3)]
        omd.setlist(1, [1, 11, 111])
        assert omd.allitems() == [(1, 1), (3, 3), (1, 11), (1, 111)]
        omd.addlist(1, [1111])
        omd = omdict([(1, 1), (3, 3), (1, 11), (1, 111), (1, 1111)])
        assert omd.allitems() == [(1, 1), (3, 3), (1, 11), (1, 111), (1, 1111)]
        omd[1] = None
        assert omd.allitems() == [(1, None), (3, 3)]

    def test_pops(self):
        init = [(1, 1), (2, 2), (1, 1), (1, 2), (1, 3)]

        # pop().
        omd = omdict(init)
        assert omd.pop(1) == 1
        assert omd.allitems() == [(2, 2)]
        assert omd.pop(_unique, 'sup') == 'sup'

        # poplist().
        omd = omdict(init)
        assert omd.poplist(1) == [1, 1, 2, 3]
        assert omd.allitems() == [(2, 2)]
        self.assertRaises(KeyError, omd.poplist, _unique)
        assert omd.poplist(_unique, 'sup') == 'sup'

        # popvalue().
        omd = omdict(init)
        assert omd.popvalue(1) == 3
        assert omd.allitems() == [(1, 1), (2, 2), (1, 1), (1, 2)]
        self.assertRaises(KeyError, omd.popvalue, _unique)
        assert omd.popvalue(_unique, default='sup') == 'sup'
        assert omd.popvalue(1, last=False) == 1
        assert omd.allitems() == [(2, 2), (1, 1), (1, 2)]

        # popitem().
        omd = omdict(init)
        assert omd.popitem() == (2, 2)
        assert omd.allitems() == [(1, 1), (1, 1), (1, 2), (1, 3)]
        assert omd.popitem() == (1, 1)
        assert omd.allitems() == []
        omd = omdict(init)
        assert omd.popitem(fromall=True) == (1, 3)
        assert omd.allitems() == [(1, 1), (2, 2), (1, 1), (1, 2)]
        assert omd.popitem(fromall=True, last=False) == (1, 1)
        assert omd.allitems() == [(2, 2), (1, 1), (1, 2)]

    def test_splats(self):
        items = [('1', 1), ('2', 2), ('3', 3)]
        omd = omdict(items)

        def splat(*args, **kwargs):
            return args, set(kwargs.items())

        assert splat(*omd, **omd) == (tuple(i[0] for i in items), set(items))


class TestBinaryOperators(unittest.TestCase):

    @property
    def _items(self):
        original = (1, ['a']), (2, 'b')
        one_different = (1, ['a']), (3, 'd')
        all_different = (1, 'c'), (3, 'd')
        duplicate_key = (1, ['a']), (1, 'e')
        empty = tuple()
        return original, one_different, all_different, duplicate_key, empty

    @property
    def _or_params(self):
        original, one_diff, all_diff, dupe_key, empty = self._items
        return [
            # self, other, other as dict, other as omdict.
            (original, original, original + original, original + original),
            (original, one_diff, original + one_diff, original + one_diff),
            (original, all_diff, original + all_diff, original + all_diff),
            (original, dupe_key, original + ((1, 'e'),), original + dupe_key),
            (original, empty, original, original),
        ]

    def test_or(self):
        for s, t, d, o in self._or_params:
            assert omdict(s) | dict(t) == omdict(d)
            assert omdict(s) | omdict(t) == omdict(o)

    def test_ior(self):
        for s, t, d, o in self._or_params:
            # Test with dict.
            a = omdict(s)
            a |= dict(t)
            assert a == omdict(d)
            # Test with omdict.
            a = omdict(s)
            a |= omdict(t)
            assert a == omdict(o)


class TestUtilities(unittest.TestCase):

    def test_rfind(self):
        tests = [([], 1, -1), ([1], 1, 0), ([1, 2], 2, 1),
                 ([1, 2, 1, 2], 1, 2), ([1, 2, 3], 4, -1), ([1, 2, 3], 1, 0)]
        for lst, item, pos in tests:
            assert _rfind(lst, item) == pos

    def test_rremove(self):
        tests = [([1, 1], 1, [1]), ([1], 1, []), ([1, 2], 2, [1]),
                 ([1, 2, 3], 1, [2, 3]), ([1, 2, 1, 2], 1, [1, 2, 2]),
                 ([1, 2, 1], 1, [1, 2])]
        for lst, item, result in tests:
            _rremove(lst, item)
            assert lst == result

        nonitems = [None, 'asdf', object(), 1000000]
        for nonitem in nonitems:
            self.assertRaises(ValueError, _rremove, lst, nonitem)


def _rfind(lst, item):
    """
    Returns the index of the last occurance of <item> in <lst>. Returns -1 if
    <item> is not in <l>.
      ex: _rfind([1,2,1,2], 1) == 2
    """
    try:
        return (len(lst) - 1) - lst[::-1].index(item)
    except ValueError:
        return -1


def _rremove(lst, item):
    """
    Removes the last occurance of <item> in <lst>, or raises a ValueError if
    <item> is not in <list>.
      ex: _rremove([1,2,1,2], 1) == [1,2,2]
    """
    pos = _rfind(lst, item)
    if pos >= 0:
        lst.pop(pos)
        return lst
    raise ValueError('_rremove(list, x): x not in list')