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