1import pytest
2from unittest import mock
3from deepdiff.helper import number_to_string, CannotCompare
4from deepdiff import DeepDiff
5from decimal import Decimal
6from deepdiff.deephash import sha256hex
7from tests import CustomClass2
8
9
10class TestIgnoreOrder:
11
12    @pytest.mark.parametrize("t1, t2, significant_digits, ignore_order, result", [
13        (10, 10.0, 5, False, {}),
14        ({10: 'a', 11.1: 'b'}, {10.0: 'a', Decimal('11.1000003'): 'b'}, 5, False, {}),
15    ])
16    def test_type_change_numeric_ignored(self, t1, t2, significant_digits, ignore_order, result):
17        ddiff = DeepDiff(t1, t2, ignore_numeric_type_changes=True,
18                         significant_digits=significant_digits, ignore_order=ignore_order)
19        assert result == ddiff
20
21    @pytest.mark.parametrize("t1, t2, expected_result",
22                             [
23                                 (10, 10.0, {}),
24                                 (10, 10.2, {'values_changed': {'root': {'new_value': 10.2, 'old_value': 10}}}),
25                                 (Decimal(10), 10.0, {}),
26                                 ({"a": Decimal(10), "b": 12, 11.0: None}, {b"b": 12, "a": 10.0, Decimal(11): None}, {}),
27                             ])
28    def test_type_change_numeric_when_ignore_order(self, t1, t2, expected_result):
29        ddiff = DeepDiff(t1, t2, ignore_order=True, ignore_numeric_type_changes=True, ignore_string_type_changes=True)
30        assert expected_result == ddiff
31
32    def test_ignore_order_depth1(self):
33        t1 = [{1, 2, 3}, {4, 5}]
34        t2 = [{4, 5, 6}, {1, 2, 3}]
35        ddiff = DeepDiff(t1, t2, ignore_order=True)
36        assert {'set_item_added': ["root[1][6]"]} == ddiff
37
38    def test_ignore_order_depth2(self):
39        t1 = [[1, 2, 3], [4, 5]]
40        t2 = [[4, 5, 6], [1, 2, 3]]
41        ddiff = DeepDiff(t1, t2, ignore_order=True)
42        assert {'iterable_item_added': {'root[1][2]': 6}} == ddiff
43
44    def test_ignore_order_depth3(self):
45        t1 = [{1, 2, 3}, [{4, 5}]]
46        t2 = [[{4, 5, 6}], {1, 2, 3}]
47        ddiff = DeepDiff(t1, t2, ignore_order=True)
48        assert {'set_item_added': ["root[1][0][6]"]} == ddiff
49
50    def test_ignore_order_depth4(self):
51        t1 = [[1, 2, 3, 4], [4, 2, 2, 1]]
52        t2 = [[4, 1, 1, 1], [1, 3, 2, 4]]
53        ddiff = DeepDiff(t1, t2, ignore_order=True)
54        assert {'iterable_item_removed': {'root[1][1]': 2}} == ddiff
55
56    def test_ignore_order_depth5(self):
57        t1 = [4, 2, 2, 1]
58        t2 = [4, 1, 1, 1]
59        ddiff = DeepDiff(t1, t2, ignore_order=True, report_repetition=True, cache_purge_level=0)
60        expected = {
61            'iterable_item_removed': {
62                'root[1]': 2,
63                'root[2]': 2
64            },
65            'repetition_change': {
66                'root[3]': {
67                    'old_repeat': 1,
68                    'new_repeat': 3,
69                    'old_indexes': [3],
70                    'new_indexes': [1, 2, 3],
71                    'value': 1
72                }
73            }
74        }
75        assert expected == ddiff
76
77        ddiff = DeepDiff(t1, t2, ignore_order=True, report_repetition=False, cache_purge_level=0)
78        dist = ddiff._get_rough_distance()
79        assert 0.1 == dist
80
81    def test_ignore_order_depth6(self):
82        t1 = [[1, 2, 3, 4], [4, 2, 2, 1]]
83        t2 = [[4, 1, 1, 1], [1, 3, 2, 4]]
84        ddiff = DeepDiff(t1, t2, ignore_order=True, report_repetition=True)
85        expected = {
86            'iterable_item_removed': {
87                'root[1][1]': 2,
88                'root[1][2]': 2
89            },
90            'repetition_change': {
91                'root[1][3]': {
92                    'old_repeat': 1,
93                    'new_repeat': 3,
94                    'old_indexes': [3],
95                    'new_indexes': [1, 2, 3],
96                    'value': 1
97                }
98            }
99        }
100
101        assert expected == ddiff
102
103    def test_list_difference_ignore_order(self):
104        t1 = {1: 1, 4: {"a": "hello", "b": [1, 2, 3]}}
105        t2 = {1: 1, 4: {"a": "hello", "b": [1, 3, 2, 3]}}
106        ddiff = DeepDiff(t1, t2, ignore_order=True)
107        assert {} == ddiff
108
109    @pytest.mark.parametrize('t1_0, t2_0', [
110        (1, 2),
111        (True, False),
112        ('a', 'b'),
113    ])
114    def test_list_difference_of_bool_only_ignore_order(self, t1_0, t2_0):
115        t1 = [t1_0]
116        t2 = [t2_0]
117        ddiff = DeepDiff(t1, t2, ignore_order=True)
118        result = {'values_changed': {'root[0]': {'new_value': t2_0, 'old_value': t1_0}}}
119        assert result == ddiff
120
121    def test_dictionary_difference_ignore_order(self):
122        t1 = {"a": [[{"b": 2, "c": 4}, {"b": 2, "c": 3}]]}
123        t2 = {"a": [[{"b": 2, "c": 3}, {"b": 2, "c": 4}]]}
124        ddiff = DeepDiff(t1, t2, ignore_order=True)
125        assert {} == ddiff
126
127    def test_nested_list_ignore_order(self):
128        t1 = [1, 2, [3, 4]]
129        t2 = [[4, 3, 3], 2, 1]
130        ddiff = DeepDiff(t1, t2, ignore_order=True)
131        assert {} == ddiff
132
133    def test_nested_list_difference_ignore_order(self):
134        t1 = [1, 2, [3, 4]]
135        t2 = [[4, 3], 2, 1]
136        ddiff = DeepDiff(t1, t2, ignore_order=True)
137        assert {} == ddiff
138
139    def test_nested_list_with_dictionarry_difference_ignore_order(self):
140        t1 = [1, 2, [3, 4, {1: 2}]]
141        t2 = [[4, 3, {1: 2}], 2, 1]
142
143        ddiff = DeepDiff(t1, t2, ignore_order=True)
144
145        result = {}
146        assert result == ddiff
147
148    def test_list_difference_ignore_order_report_repetition(self):
149        t1 = [1, 3, 1, 4]
150        t2 = [4, 4, 1]
151        ddiff = DeepDiff(t1, t2, ignore_order=True, report_repetition=True)
152        result = {
153            'iterable_item_removed': {
154                'root[1]': 3
155            },
156            'repetition_change': {
157                'root[0]': {
158                    'old_repeat': 2,
159                    'old_indexes': [0, 2],
160                    'new_indexes': [2],
161                    'value': 1,
162                    'new_repeat': 1
163                },
164                'root[3]': {
165                    'old_repeat': 1,
166                    'old_indexes': [3],
167                    'new_indexes': [0, 1],
168                    'value': 4,
169                    'new_repeat': 2
170                }
171            }
172        }
173        assert result == ddiff
174
175    # TODO: fix repeition report
176    def test_nested_list_ignore_order_report_repetition_wrong_currently(self):
177        t1 = [1, 2, [3, 4]]
178        t2 = [[4, 3, 3], 2, 1]
179        ddiff = DeepDiff(t1, t2, ignore_order=True, report_repetition=True)
180        result = {
181            'repetition_change': {
182                'root[2][0]': {
183                    'old_repeat': 1,
184                    'new_indexes': [1, 2],
185                    'old_indexes': [1],
186                    'value': 3,
187                    'new_repeat': 2
188                }
189            }
190        }
191        assert result != ddiff
192
193    def test_list_of_unhashable_difference_ignore_order(self):
194        t1 = [{"a": 2}, {"b": [3, 4, {1: 1}]}]
195        t2 = [{"b": [3, 4, {1: 1}]}, {"a": 2}]
196        ddiff = DeepDiff(t1, t2, ignore_order=True)
197        assert {} == ddiff
198
199    def test_list_of_unhashable_difference_ignore_order2(self):
200        t1 = [1, {"a": 2}, {"b": [3, 4, {1: 1}]}, "B"]
201        t2 = [{"b": [3, 4, {1: 1}]}, {"a": 2}, {1: 1}]
202        ddiff = DeepDiff(t1, t2, ignore_order=True)
203        result = {
204            'iterable_item_added': {
205                'root[2]': {
206                    1: 1
207                }
208            },
209            'iterable_item_removed': {
210                'root[3]': 'B',
211                'root[0]': 1
212            }
213        }
214        assert result == ddiff
215
216    def test_list_of_unhashable_difference_ignore_order3(self):
217        t1 = [1, {"a": 2}, {"a": 2}, {"b": [3, 4, {1: 1}]}, "B"]
218        t2 = [{"b": [3, 4, {1: 1}]}, {1: 1}]
219        ddiff = DeepDiff(t1, t2, ignore_order=True)
220        result = {
221            'values_changed': {
222                'root[1]': {
223                    'new_value': {
224                        1: 1
225                    },
226                    'old_value': {
227                        'a': 2
228                    }
229                }
230            },
231            'iterable_item_removed': {
232                'root[0]': 1,
233                'root[4]': 'B'
234            }
235        }
236        assert result == ddiff
237
238    def test_list_of_unhashable_difference_ignore_order_report_repetition(
239            self):
240        t1 = [1, {"a": 2}, {"a": 2}, {"b": [3, 4, {1: 1}]}, "B"]
241        t2 = [{"b": [3, 4, {1: 1}]}, {1: 1}]
242        ddiff = DeepDiff(t1, t2, ignore_order=True, report_repetition=True)
243        result = {
244            'iterable_item_added': {
245                'root[1]': {
246                    1: 1
247                }
248            },
249            'iterable_item_removed': {
250                'root[4]': 'B',
251                'root[0]': 1,
252                'root[1]': {
253                    'a': 2
254                },
255                'root[2]': {
256                    'a': 2
257                }
258            }
259        }
260        assert result == ddiff
261
262    def test_list_of_unhashable_difference_ignore_order4(self):
263        t1 = [{"a": 2}, {"a": 2}]
264        t2 = [{"a": 2}]
265        ddiff = DeepDiff(t1, t2, ignore_order=True)
266        result = {}
267        assert result == ddiff
268
269    def test_list_of_unhashable_difference_ignore_order_report_repetition2(
270            self):
271        t1 = [{"a": 2}, {"a": 2}]
272        t2 = [{"a": 2}]
273        ddiff = DeepDiff(t1, t2, ignore_order=True, report_repetition=True)
274        result = {
275            'repetition_change': {
276                'root[0]': {
277                    'old_repeat': 2,
278                    'new_indexes': [0],
279                    'old_indexes': [0, 1],
280                    'value': {
281                        'a': 2
282                    },
283                    'new_repeat': 1
284                }
285            }
286        }
287        assert result == ddiff
288
289    def test_list_ignore_order_report_repetition(self):
290        t1 = [5, 1, 3, 1, 4, 4, 6]
291        t2 = [7, 4, 4, 1, 3, 4, 8]
292        ddiff = DeepDiff(t1, t2, ignore_order=True, report_repetition=True)
293        result = {
294            'values_changed': {
295                'root[6]': {
296                    'new_value': 7,
297                    'old_value': 6
298                },
299                'root[0]': {
300                    'new_value': 8,
301                    'old_value': 5
302                }
303            },
304            'repetition_change': {
305                'root[4]': {
306                    'old_repeat': 2,
307                    'new_repeat': 3,
308                    'old_indexes': [4, 5],
309                    'new_indexes': [1, 2, 5],
310                    'value': 4
311                },
312                'root[1]': {
313                    'old_repeat': 2,
314                    'new_repeat': 1,
315                    'old_indexes': [1, 3],
316                    'new_indexes': [3],
317                    'value': 1
318                }
319            }
320        }
321        assert result == ddiff
322
323    def test_list_of_sets_difference_ignore_order(self):
324        t1 = [{1}, {2}, {3}]
325        t2 = [{4}, {1}, {2}, {3}]
326        ddiff = DeepDiff(t1, t2, ignore_order=True)
327        result = {'iterable_item_added': {'root[0]': {4}}}
328        assert result == ddiff
329
330    def test_list_of_sets_difference_ignore_order_when_there_is_duplicate(
331            self):
332        t1 = [{1}, {2}, {3}]
333        t2 = [{4}, {1}, {2}, {3}, {3}]
334        ddiff = DeepDiff(t1, t2, ignore_order=True)
335        result = {'iterable_item_added': {'root[0]': {4}}}
336        assert result == ddiff
337
338    def test_list_of_sets_difference_ignore_order_when_there_is_duplicate_and_mix_of_hashable_unhashable(
339            self):
340        t1 = [1, 1, {2}, {3}]
341        t2 = [{4}, 1, {2}, {3}, {3}, 1, 1]
342        ddiff = DeepDiff(t1, t2, ignore_order=True)
343        result = {'iterable_item_added': {'root[0]': {4}}}
344        assert result == ddiff
345
346    def test_dictionary_of_list_of_dictionary_ignore_order(self):
347        t1 = {
348            'item': [{
349                'title': 1,
350                'http://purl.org/rss/1.0/modules/content/:encoded': '1'
351            }, {
352                'title': 2,
353                'http://purl.org/rss/1.0/modules/content/:encoded': '2'
354            }]
355        }
356
357        t2 = {
358            'item': [{
359                'http://purl.org/rss/1.0/modules/content/:encoded': '1',
360                'title': 1
361            }, {
362                'http://purl.org/rss/1.0/modules/content/:encoded': '2',
363                'title': 2
364            }]
365        }
366
367        ddiff = DeepDiff(t1, t2, ignore_order=True)
368        assert {} == ddiff
369
370    def test_comprehensive_ignore_order(self):
371
372        t1 = {
373            'key1': 'val1',
374            'key2': [
375                {
376                    'key3': 'val3',
377                    'key4': 'val4',
378                },
379                {
380                    'key5': 'val5',
381                    'key6': 'val6',
382                },
383            ],
384        }
385
386        t2 = {
387            'key1': 'val1',
388            'key2': [
389                {
390                    'key5': 'val5',
391                    'key6': 'val6',
392                },
393                {
394                    'key3': 'val3',
395                    'key4': 'val4',
396                },
397            ],
398        }
399
400        ddiff = DeepDiff(t1, t2, ignore_order=True)
401        assert {} == ddiff
402
403    def test_ignore_order_when_objects_similar(self):
404
405        t1 = {
406            'key1': 'val1',
407            'key2': [
408                {
409                    'key3': 'val3',
410                    'key4': 'val4',
411                },
412                {
413                    'key5': 'val5',
414                    'key6': 'val6',
415                },
416            ],
417        }
418
419        t2 = {
420            'key1': 'val1',
421            'key2': [
422                {
423                    'key5': 'CHANGE',
424                    'key6': 'val6',
425                },
426                {
427                    'key3': 'val3',
428                    'key4': 'val4',
429                },
430            ],
431        }
432
433        ddiff = DeepDiff(t1, t2, ignore_order=True)
434        assert {'values_changed': {"root['key2'][1]['key5']": {'new_value': 'CHANGE', 'old_value': 'val5'}}} == ddiff
435
436    def test_set_ignore_order_report_repetition(self):
437        """
438        If this test fails, it means that DeepDiff is not checking
439        for set types before general iterables.
440        So it forces creating the hashtable because of report_repetition=True.
441        """
442        t1 = {2, 1, 8}
443        t2 = {1, 2, 3, 5}
444        ddiff = DeepDiff(t1, t2, ignore_order=True, report_repetition=True)
445        result = {
446            'set_item_added': {'root[3]', 'root[5]'},
447            'set_item_removed': {'root[8]'}
448        }
449        assert result == ddiff
450
451    def test_custom_objects2(self):
452        cc_a = CustomClass2(prop1=["a"], prop2=["b"])
453        cc_b = CustomClass2(prop1=["b"], prop2=["b"])
454        t1 = [cc_a, CustomClass2(prop1=["c"], prop2=["d"])]
455        t2 = [cc_b, CustomClass2(prop1=["c"], prop2=["d"])]
456
457        ddiff = DeepDiff(t1, t2, ignore_order=True)
458
459        result = {'values_changed': {'root[0].prop1[0]': {'new_value': 'b', 'old_value': 'a'}}}
460        assert result == ddiff
461
462    def test_custom_object_type_change_when_ignore_order(self):
463
464        class Burrito:
465            bread = 'flour'
466
467            def __init__(self):
468                self.spicy = True
469
470        class Taco:
471            bread = 'flour'
472
473            def __init__(self):
474                self.spicy = True
475
476        burrito = Burrito()
477        taco = Taco()
478
479        burritos = [burrito]
480        tacos = [taco]
481
482        assert not DeepDiff(burritos, tacos, ignore_type_in_groups=[(Taco, Burrito)], ignore_order=True)
483
484    def test_decimal_ignore_order(self):
485        t1 = [{1: Decimal('10.1')}, {2: Decimal('10.2')}]
486        t2 = [{2: Decimal('10.2')}, {1: Decimal('10.1')}]
487        ddiff = DeepDiff(t1, t2, ignore_order=True)
488        result = {}
489        assert result == ddiff
490
491    @pytest.mark.parametrize("t1, t2, significant_digits, ignore_order", [
492        (100000, 100021, 3, False),
493        ([10, 12, 100000], [50, 63, 100021], 3, False),
494        ([10, 12, 100000], [50, 63, 100021], 3, True),
495    ])
496    def test_number_to_string_func(self, t1, t2, significant_digits, ignore_order):
497        def custom_number_to_string(number, *args, **kwargs):
498            number = 100 if number < 100 else number
499            return number_to_string(number, *args, **kwargs)
500
501        ddiff = DeepDiff(t1, t2, significant_digits=3, number_format_notation="e",
502                         number_to_string_func=custom_number_to_string)
503
504        assert {} == ddiff
505
506    def test_ignore_type_in_groups_numbers_and_strings_when_ignore_order(self):
507        t1 = [1, 2, 3, 'a']
508        t2 = [1.0, 2.0, 3.3, b'a']
509        ddiff = DeepDiff(t1, t2, ignore_numeric_type_changes=True, ignore_string_type_changes=True, ignore_order=True)
510        result = {'values_changed': {'root[2]': {'new_value': 3.3, 'old_value': 3}}}
511        assert result == ddiff
512
513    def test_ignore_string_type_changes_when_dict_keys_merge_is_not_deterministic(self):
514        t1 = {'a': 10, b'a': 20}
515        t2 = {'a': 11, b'a': 22}
516        ddiff = DeepDiff(t1, t2, ignore_numeric_type_changes=True, ignore_string_type_changes=True, ignore_order=True)
517        result = {'values_changed': {"root['a']": {'new_value': 22, 'old_value': 20}}}
518        alternative_result = {'values_changed': {"root['a']": {'new_value': 11, 'old_value': 10}}}
519        assert result == ddiff or alternative_result == ddiff
520
521    def test_skip_exclude_path5(self):
522        exclude_paths = ["root[0]['e']", "root[1]['e']"]
523
524        t1 = [{'a': 1, 'b': 'randomString', 'e': "1111"}]
525        t2 = [{'a': 1, 'b': 'randomString', 'e': "2222"}]
526
527        ddiff = DeepDiff(t1, t2, exclude_paths=exclude_paths,
528                         ignore_order=True, report_repetition=False)
529        result = {}
530        assert result == ddiff
531
532    def test_skip_str_type_in_dict_on_list_when_ignored_order(self):
533        t1 = [{1: "a"}]
534        t2 = [{}]
535        ddiff = DeepDiff(t1, t2, exclude_types=[str], ignore_order=True)
536        result = {}
537        assert result == ddiff
538
539    @mock.patch('deepdiff.diff.logger')
540    @mock.patch('deepdiff.diff.DeepHash')
541    def test_diff_when_hash_fails(self, mock_DeepHash, mock_logger):
542        mock_DeepHash.side_effect = Exception('Boom!')
543        t1 = {"blah": {4}, 2: 1337}
544        t2 = {"blah": {4}, 2: 1337}
545        DeepDiff(t1, t2, ignore_order=True)
546        assert mock_logger.error.called
547
548    def test_bool_vs_number(self):
549        t1 = {
550            "A List": [
551                {
552                    "Value One": True,
553                    "Value Two": 1
554                }
555            ],
556        }
557
558        t2 = {
559            "A List": [
560                {
561                    "Value Two": 1,
562                    "Value One": True
563                }
564            ],
565        }
566
567        ddiff = DeepDiff(t1, t2, ignore_order=True, cutoff_intersection_for_pairs=1)
568        assert ddiff == {}
569
570    @pytest.mark.parametrize('max_passes, expected', [
571        (0, {'values_changed': {'root[0]': {'new_value': {'key5': 'CHANGE', 'key6': 'val6'}, 'old_value': {'key3': [[[[[1, 2, 4, 5]]]]], 'key4': [7, 8]}}, 'root[1]': {'new_value': {'key3': [[[[[1, 3, 5, 4]]]]], 'key4': [7, 8]}, 'old_value': {'key5': 'val5', 'key6': 'val6'}}}}),
572        (1, {'values_changed': {"root[1]['key5']": {'new_value': 'CHANGE', 'old_value': 'val5'}, "root[0]['key3'][0]": {'new_value': [[[[1, 3, 5, 4]]]], 'old_value': [[[[1, 2, 4, 5]]]]}}}),
573        (22, {'values_changed': {"root[1]['key5']": {'new_value': 'CHANGE', 'old_value': 'val5'}, "root[0]['key3'][0][0][0][0][1]": {'new_value': 3, 'old_value': 2}}})
574    ])
575    def test_ignore_order_max_passes(self, max_passes, expected):
576        t1 = [
577            {
578                'key3': [[[[[1, 2, 4, 5]]]]],
579                'key4': [7, 8],
580            },
581            {
582                'key5': 'val5',
583                'key6': 'val6',
584            },
585        ]
586
587        t2 = [
588            {
589                'key5': 'CHANGE',
590                'key6': 'val6',
591            },
592            {
593                'key3': [[[[[1, 3, 5, 4]]]]],
594                'key4': [7, 8],
595            },
596        ]
597
598        ddiff = DeepDiff(t1, t2, ignore_order=True, max_passes=max_passes, verbose_level=2, cache_size=5000, cutoff_intersection_for_pairs=1)
599        assert expected == ddiff
600
601    @pytest.mark.parametrize('max_diffs, expected', [
602        (1, {}),
603        (65, {'values_changed': {"root[1]['key5']": {'new_value': 'CHANGE', 'old_value': 'val5'}}}),
604        (80, {'values_changed': {"root[1]['key5']": {'new_value': 'CHANGE', 'old_value': 'val5'}, "root[0]['key3'][0][0][0][0][1]": {'new_value': 3, 'old_value': 2}}}),
605    ])
606    def test_ignore_order_max_diffs(self, max_diffs, expected):
607        t1 = [
608            {
609                'key3': [[[[[1, 2, 4, 5]]]]],
610                'key4': [7, 8],
611            },
612            {
613                'key5': 'val5',
614                'key6': 'val6',
615            },
616        ]
617
618        t2 = [
619            {
620                'key5': 'CHANGE',
621                'key6': 'val6',
622            },
623            {
624                'key3': [[[[[1, 3, 5, 4]]]]],
625                'key4': [7, 8],
626            },
627        ]
628
629        # Note: these tests are not exactly deterministic
630        ddiff = DeepDiff(t1, t2, ignore_order=True, max_diffs=max_diffs, verbose_level=2, cache_size=5000, cutoff_intersection_for_pairs=1)
631        assert expected == ddiff
632
633    def test_stats_that_include_distance_cache_hits(self):
634        t1 = [
635            [1, 2, 3, 9], [9, 8, 5, 9]
636        ]
637
638        t2 = [
639            [1, 2, 4, 10], [4, 2, 5]
640        ]
641
642        diff = DeepDiff(t1, t2, ignore_order=True, cache_size=5000, cutoff_intersection_for_pairs=1)
643        expected = {
644            'PASSES COUNT': 7,
645            'DIFF COUNT': 37,
646            'DISTANCE CACHE HIT COUNT': 0,
647            'MAX PASS LIMIT REACHED': False,
648            'MAX DIFF LIMIT REACHED': False,
649        }
650        assert expected == diff.get_stats()
651
652    def test_ignore_order_report_repetition_and_self_loop(self):
653        t1 = [[1, 2, 1, 3]]
654        t1.append(t1)
655
656        t2 = [[1, 2, 2, 2, 4]]
657        t2.append(t2)
658
659        diff = DeepDiff(t1, t2, ignore_order=True, cutoff_intersection_for_pairs=1)
660        expected = {
661            'values_changed': {
662                'root[0][3]': {
663                    'new_value': 4,
664                    'old_value': 3
665                },
666                'root[1]': {
667                    'new_value': t2,
668                    'old_value': t1
669                }
670            }
671        }
672        assert expected == diff
673
674        diff2 = DeepDiff(t1, t2, ignore_order=True, cache_size=5000, cutoff_intersection_for_pairs=1)
675        assert expected == diff2
676
677    def test_ignore_order_with_sha256_hash(self):
678        t1 = [
679            [1, 2, 3, 9], [9, 8, 5, 9]
680        ]
681
682        t2 = [
683            [1, 2, 3, 10], [8, 2, 5]
684        ]
685        diff = DeepDiff(t1, t2, ignore_order=True, hasher=sha256hex, cutoff_intersection_for_pairs=1)
686        expected = {
687            'values_changed': {
688                'root[0][3]': {
689                    'new_value': 10,
690                    'old_value': 9
691                },
692                'root[1][0]': {
693                    'new_value': 2,
694                    'old_value': 9
695                }
696            }
697        }
698        assert expected == diff
699
700    def test_ignore_order_cache_for_individual_distances(self):
701        t1 = [[1, 2, 'B', 3], 'B']
702        t2 = [[1, 2, 3, 5], 5]
703        diff = DeepDiff(t1, t2, ignore_order=True, cache_size=5000, cutoff_intersection_for_pairs=1)
704        expected = {
705            'values_changed': {
706                'root[1]': {
707                    'new_value': 5,
708                    'old_value': 'B'
709                }
710            },
711            'iterable_item_added': {
712                'root[0][3]': 5
713            },
714            'iterable_item_removed': {
715                'root[0][2]': 'B'
716            }
717        }
718        assert expected == diff
719
720        stats = diff.get_stats()
721        expected_stats = {
722            'PASSES COUNT': 3,
723            'DIFF COUNT': 13,
724            'DISTANCE CACHE HIT COUNT': 1,
725            'MAX PASS LIMIT REACHED': False,
726            'MAX DIFF LIMIT REACHED': False
727        }
728        assert expected_stats == stats
729
730        t1 = [[1, 2, 'B', 3], 5]
731        t2 = [[1, 2, 3, 5], 'B']
732        diff2 = DeepDiff(t1, t2, ignore_order=True, cache_size=5000, cutoff_intersection_for_pairs=1)
733        assert expected_stats == diff2.get_stats()
734
735    def test_cutoff_distance_for_pairs(self):
736        t1 = [[1.0]]
737        t2 = [[20.0]]
738        diff1 = DeepDiff(t1, t2, ignore_order=True, cutoff_distance_for_pairs=0.3)
739        expected1 = {'values_changed': {'root[0][0]': {'new_value': 20.0, 'old_value': 1.0}}}
740        assert expected1 == diff1
741
742        diff2 = DeepDiff(t1, t2, ignore_order=True, cutoff_distance_for_pairs=0.1)
743        expected2 = {'values_changed': {'root[0]': {'new_value': [20.0], 'old_value': [1.0]}}}
744        assert expected2 == diff2
745
746        diff_with_dist = DeepDiff(1.0, 20.0, get_deep_distance=True)
747        expected = {'values_changed': {'root': {'new_value': 20.0, 'old_value': 1.0}}, 'deep_distance': 0.2714285714285714}
748
749        assert expected == diff_with_dist
750
751    def test_ignore_order_and_group_by(self):
752        t1 = [
753            {'id': 'AA', 'name': 'Joe', 'ate': ['Nothing']},
754            {'id': 'BB', 'name': 'James', 'ate': ['Chips', 'Cheese']},
755            {'id': 'CC', 'name': 'Mike', 'ate': ['Apple']},
756        ]
757
758        t2 = [
759            {'id': 'BB', 'name': 'James', 'ate': ['Chips', 'Brownies', 'Cheese']},
760            {'id': 'AA', 'name': 'Joe', 'ate': ['Nothing']},
761            {'id': 'CC', 'name': 'Mike', 'ate': ['Apple', 'Apple']},
762        ]
763
764        diff = DeepDiff(t1, t2, group_by='id', ignore_order=False)
765        expected = {
766            'values_changed': {
767                "root['BB']['ate'][1]": {
768                    'new_value': 'Brownies',
769                    'old_value': 'Cheese'
770                }
771            },
772            'iterable_item_added': {
773                "root['CC']['ate'][1]": 'Apple',
774                "root['BB']['ate'][2]": 'Cheese'
775            }
776        }
777        assert expected == diff
778
779        diff2 = DeepDiff(t1, t2, group_by='id', ignore_order=True)
780        expected2 = {'iterable_item_added': {"root['BB']['ate'][1]": 'Brownies'}}
781        assert expected2 == diff2
782
783
784class TestCompareFuncIgnoreOrder:
785
786    def test_ignore_order_with_compare_func_to_guide_comparison(self):
787        t1 = [
788            {
789                'id': 1,
790                'value': [1]
791            },
792            {
793                'id': 2,
794                'value': [7, 8, 1]
795            },
796            {
797                'id': 3,
798                'value': [7, 8],
799            },
800        ]
801
802        t2 = [
803            {
804                'id': 2,
805                'value': [7, 8]
806            },
807            {
808                'id': 3,
809                'value': [7, 8, 1],
810            },
811            {
812                'id': 1,
813                'value': [1]
814            },
815        ]
816
817        expected = {
818            'values_changed': {
819                "root[2]['id']": {
820                    'new_value': 2,
821                    'old_value': 3
822                },
823                "root[1]['id']": {
824                    'new_value': 3,
825                    'old_value': 2
826                }
827            }
828        }
829
830        expected_with_compare_func = {
831            'iterable_item_added': {
832                "root[2]['value'][2]": 1
833            },
834            'iterable_item_removed': {
835                "root[1]['value'][2]": 1
836            }
837        }
838
839        ddiff = DeepDiff(t1, t2, ignore_order=True)
840
841        assert expected == ddiff
842
843        def compare_func(x, y, level=None):
844            try:
845                return x['id'] == y['id']
846            except Exception:
847                raise CannotCompare() from None
848
849        ddiff2 = DeepDiff(t1, t2, ignore_order=True, iterable_compare_func=compare_func)
850        assert expected_with_compare_func == ddiff2
851        assert ddiff != ddiff2
852
853        ddiff3 = DeepDiff(t1, t2, ignore_order=True, iterable_compare_func=compare_func, view='tree')
854        assert 1 == ddiff3['iterable_item_removed'][0].t1
855        assert 1 == ddiff3['iterable_item_added'][0].t2
856
857    def test_ignore_order_with_compare_func_can_throw_cannot_compare(self):
858        t1 = [
859            {1},
860            {
861                'id': 2,
862                'value': [7, 8, 1]
863            },
864            {
865                'id': 3,
866                'value': [7, 8],
867            },
868        ]
869
870        t2 = [
871            {
872                'id': 2,
873                'value': [7, 8]
874            },
875            {
876                'id': 3,
877                'value': [7, 8, 1],
878            },
879            {},
880        ]
881
882        expected = {
883            'type_changes': {
884                'root[0]': {
885                    'old_type': set,
886                    'new_type': dict,
887                    'old_value': {1},
888                    'new_value': {}
889                }
890            },
891            'values_changed': {
892                "root[2]['id']": {
893                    'new_value': 2,
894                    'old_value': 3
895                },
896                "root[1]['id']": {
897                    'new_value': 3,
898                    'old_value': 2
899                }
900            }
901        }
902        expected_with_compare_func = {
903            'type_changes': {
904                'root[0]': {
905                    'old_type': set,
906                    'new_type': dict,
907                    'old_value': {1},
908                    'new_value': {}
909                }
910            },
911            'iterable_item_added': {
912                "root[2]['value'][2]": 1
913            },
914            'iterable_item_removed': {
915                "root[1]['value'][2]": 1
916            }
917        }
918
919        ddiff = DeepDiff(t1, t2, cutoff_intersection_for_pairs=1, cutoff_distance_for_pairs=1, ignore_order=True)
920        assert expected == ddiff
921
922        def compare_func(x, y, level=None):
923            try:
924                return x['id'] == y['id']
925            except Exception:
926                raise CannotCompare() from None
927
928        ddiff2 = DeepDiff(t1, t2, ignore_order=True, cutoff_intersection_for_pairs=1, cutoff_distance_for_pairs=1, iterable_compare_func=compare_func)
929        assert expected_with_compare_func == ddiff2
930        assert ddiff != ddiff2
931
932
933class TestDynamicIgnoreOrder:
934    def test_ignore_order_func(self):
935        t1 = {
936            "order_matters": [
937                {1},
938                {
939                    'id': 2,
940                    'value': [7, 8, 1]
941                },
942                {
943                    'id': 3,
944                    'value': [7, 8],
945                },
946            ],
947            "order_does_not_matter": [
948                {1},
949                {
950                    'id': 2,
951                    'value': [7, 8, 1]
952                },
953                {
954                    'id': 3,
955                    'value': [7, 8],
956                },
957            ]
958        }
959
960        t2 = {
961            "order_matters": [
962                {
963                    'id': 2,
964                    'value': [7, 8]
965                },
966                {
967                    'id': 3,
968                    'value': [7, 8, 1],
969                },
970                {},
971            ],
972            "order_does_not_matter": [
973                {
974                    'id': 2,
975                    'value': [7, 8]
976                },
977                {
978                    'id': 3,
979                    'value': [7, 8, 1],
980                },
981                {},
982            ]
983        }
984
985        def ignore_order_func(level):
986            return "order_does_not_matter" in level.path()
987
988        ddiff = DeepDiff(t1, t2, cutoff_intersection_for_pairs=1, cutoff_distance_for_pairs=1, ignore_order_func=ignore_order_func)
989
990        expected = {
991            'type_changes': {
992                "root['order_matters'][0]": {
993                    'old_type': set,
994                    'new_type': dict,
995                    'old_value': {1},
996                    'new_value': {'id': 2, 'value': [7, 8]}
997                },
998                "root['order_does_not_matter'][0]": {
999                    'old_type': set,
1000                    'new_type': dict,
1001                    'old_value': {1},
1002                    'new_value': {}
1003                }
1004            },
1005            'dictionary_item_removed': [
1006                "root['order_matters'][2]['id']",
1007                "root['order_matters'][2]['value']"
1008            ],
1009            'values_changed': {
1010                "root['order_matters'][1]['id']": {'new_value': 3, 'old_value': 2},
1011                "root['order_does_not_matter'][2]['id']": {'new_value': 2, 'old_value': 3},
1012                "root['order_does_not_matter'][1]['id']": {'new_value': 3, 'old_value': 2}
1013            }
1014        }
1015        assert expected == ddiff
1016