1/* 2Copyright 2018 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package typed_test 18 19import ( 20 "fmt" 21 "testing" 22 23 "sigs.k8s.io/structured-merge-diff/v3/fieldpath" 24 "sigs.k8s.io/structured-merge-diff/v3/typed" 25) 26 27type symdiffTestCase struct { 28 name string 29 rootTypeName string 30 schema typed.YAMLObject 31 quints []symdiffQuint 32} 33 34type symdiffQuint struct { 35 lhs typed.YAMLObject 36 rhs typed.YAMLObject 37 38 // Please note that everything is tested both ways--removed and added 39 // are symmetric. So if a test case is covered for one of them, it 40 // covers both. 41 removed *fieldpath.Set 42 modified *fieldpath.Set 43 added *fieldpath.Set 44} 45 46var symdiffCases = []symdiffTestCase{{ 47 name: "simple pair", 48 rootTypeName: "stringPair", 49 schema: `types: 50- name: stringPair 51 map: 52 fields: 53 - name: key 54 type: 55 scalar: string 56 - name: value 57 type: 58 namedType: __untyped_atomic_ 59- name: __untyped_atomic_ 60 scalar: untyped 61 list: 62 elementType: 63 namedType: __untyped_atomic_ 64 elementRelationship: atomic 65 map: 66 elementType: 67 namedType: __untyped_atomic_ 68 elementRelationship: atomic 69`, 70 quints: []symdiffQuint{{ 71 lhs: `{"key":"foo","value":1}`, 72 rhs: `{"key":"foo","value":1}`, 73 removed: _NS(), 74 modified: _NS(), 75 added: _NS(), 76 }, { 77 lhs: `{"key":"foo","value":{}}`, 78 rhs: `{"key":"foo","value":1}`, 79 removed: _NS(), 80 modified: _NS(_P("value")), 81 added: _NS(), 82 }, { 83 lhs: `{"key":"foo","value":1}`, 84 rhs: `{"key":"foo","value":{}}`, 85 removed: _NS(), 86 modified: _NS(_P("value")), 87 added: _NS(), 88 }, { 89 lhs: `{"key":"foo","value":1}`, 90 rhs: `{"key":"foo","value":{"doesn't matter":"what's here","or":{"how":"nested"}}}`, 91 removed: _NS(), 92 modified: _NS(_P("value")), 93 added: _NS(), 94 }, { 95 lhs: `{"key":"foo","value":null}`, 96 rhs: `{"key":"foo","value":{}}`, 97 removed: _NS(), 98 modified: _NS(_P("value")), 99 added: _NS(), 100 }, { 101 lhs: `{"key":"foo"}`, 102 rhs: `{"value":true}`, 103 removed: _NS(_P("key")), 104 modified: _NS(), 105 added: _NS(_P("value")), 106 }, { 107 lhs: `{"key":"foot"}`, 108 rhs: `{"key":"foo","value":true}`, 109 removed: _NS(), 110 modified: _NS(_P("key")), 111 added: _NS(_P("value")), 112 }}, 113}, { 114 name: "null/empty map", 115 rootTypeName: "nestedMap", 116 schema: `types: 117- name: nestedMap 118 map: 119 fields: 120 - name: inner 121 type: 122 map: 123 elementType: 124 namedType: __untyped_atomic_ 125- name: __untyped_atomic_ 126 scalar: untyped 127 list: 128 elementType: 129 namedType: __untyped_atomic_ 130 elementRelationship: atomic 131 map: 132 elementType: 133 namedType: __untyped_atomic_ 134 elementRelationship: atomic 135`, 136 quints: []symdiffQuint{{ 137 lhs: `{}`, 138 rhs: `{"inner":{}}`, 139 removed: _NS(), 140 modified: _NS(), 141 added: _NS(_P("inner")), 142 }, { 143 lhs: `{}`, 144 rhs: `{"inner":null}`, 145 removed: _NS(), 146 modified: _NS(), 147 added: _NS(_P("inner")), 148 }, { 149 lhs: `{"inner":null}`, 150 rhs: `{"inner":{}}`, 151 removed: _NS(), 152 modified: _NS(_P("inner")), 153 added: _NS(), 154 }, { 155 lhs: `{"inner":{}}`, 156 rhs: `{"inner":null}`, 157 removed: _NS(), 158 modified: _NS(_P("inner")), 159 added: _NS(), 160 }, { 161 lhs: `{"inner":{}}`, 162 rhs: `{"inner":{}}`, 163 removed: _NS(), 164 modified: _NS(), 165 added: _NS(), 166 }}, 167}, { 168 name: "null/empty struct", 169 rootTypeName: "nestedStruct", 170 schema: `types: 171- name: nestedStruct 172 map: 173 fields: 174 - name: inner 175 type: 176 map: 177 fields: 178 - name: value 179 type: 180 namedType: __untyped_atomic_ 181`, 182 quints: []symdiffQuint{{ 183 lhs: `{}`, 184 rhs: `{"inner":{}}`, 185 removed: _NS(), 186 modified: _NS(), 187 added: _NS(_P("inner")), 188 }, { 189 lhs: `{}`, 190 rhs: `{"inner":null}`, 191 removed: _NS(), 192 modified: _NS(), 193 added: _NS(_P("inner")), 194 }, { 195 lhs: `{"inner":null}`, 196 rhs: `{"inner":{}}`, 197 removed: _NS(), 198 modified: _NS(_P("inner")), 199 added: _NS(), 200 }, { 201 lhs: `{"inner":{}}`, 202 rhs: `{"inner":null}`, 203 removed: _NS(), 204 modified: _NS(_P("inner")), 205 added: _NS(), 206 }, { 207 lhs: `{"inner":{}}`, 208 rhs: `{"inner":{}}`, 209 removed: _NS(), 210 modified: _NS(), 211 added: _NS(), 212 }}, 213}, { 214 name: "null/empty list", 215 rootTypeName: "nestedList", 216 schema: `types: 217- name: nestedList 218 map: 219 fields: 220 - name: inner 221 type: 222 list: 223 elementType: 224 namedType: __untyped_atomic_ 225 elementRelationship: atomic 226- name: __untyped_atomic_ 227 scalar: untyped 228 list: 229 elementType: 230 namedType: __untyped_atomic_ 231 elementRelationship: atomic 232 map: 233 elementType: 234 namedType: __untyped_atomic_ 235 elementRelationship: atomic 236`, 237 quints: []symdiffQuint{{ 238 lhs: `{}`, 239 rhs: `{"inner":[]}`, 240 removed: _NS(), 241 modified: _NS(), 242 added: _NS(_P("inner")), 243 }, { 244 lhs: `{}`, 245 rhs: `{"inner":null}`, 246 removed: _NS(), 247 modified: _NS(), 248 added: _NS(_P("inner")), 249 }, { 250 lhs: `{"inner":null}`, 251 rhs: `{"inner":[]}`, 252 removed: _NS(), 253 modified: _NS(_P("inner")), 254 added: _NS(), 255 }, { 256 lhs: `{"inner":[]}`, 257 rhs: `{"inner":null}`, 258 removed: _NS(), 259 modified: _NS(_P("inner")), 260 added: _NS(), 261 }, { 262 lhs: `{"inner":[]}`, 263 rhs: `{"inner":[]}`, 264 removed: _NS(), 265 modified: _NS(), 266 added: _NS(), 267 }}, 268}, { 269 name: "map merge", 270 rootTypeName: "nestedMap", 271 schema: `types: 272- name: nestedMap 273 map: 274 elementType: 275 namedType: nestedMap 276`, 277 quints: []symdiffQuint{{ 278 lhs: `{"a":{},"b":{}}`, 279 rhs: `{"a":{},"b":{}}`, 280 removed: _NS(), 281 modified: _NS(), 282 added: _NS(), 283 }, { 284 lhs: `{"a":{}}`, 285 rhs: `{"b":{}}`, 286 removed: _NS(_P("a")), 287 modified: _NS(), 288 added: _NS(_P("b")), 289 }, { 290 lhs: `{"a":{"b":{"c":{}}}}`, 291 rhs: `{"a":{"b":{}}}`, 292 removed: _NS(_P("a", "b", "c")), 293 modified: _NS(), 294 added: _NS(), 295 }, { 296 lhs: `{"a":{}}`, 297 rhs: `{"a":{"b":{}}}`, 298 removed: _NS(), 299 modified: _NS(), 300 added: _NS(_P("a", "b")), 301 }}, 302}, { 303 name: "untyped deduced", 304 rootTypeName: "__untyped_deduced_", 305 schema: `types: 306- name: __untyped_atomic_ 307 scalar: untyped 308 list: 309 elementType: 310 namedType: __untyped_atomic_ 311 elementRelationship: atomic 312 map: 313 elementType: 314 namedType: __untyped_atomic_ 315 elementRelationship: atomic 316- name: __untyped_deduced_ 317 scalar: untyped 318 list: 319 elementType: 320 namedType: __untyped_atomic_ 321 elementRelationship: atomic 322 map: 323 elementType: 324 namedType: __untyped_deduced_ 325 elementRelationship: separable 326`, 327 quints: []symdiffQuint{{ 328 lhs: `{"a":{}}}`, 329 rhs: `{"a":{"b":{}}}`, 330 removed: _NS(), 331 modified: _NS(), 332 added: _NS(_P("a", "b")), 333 }, { 334 lhs: `{"a":null}`, 335 rhs: `{"a":{"b":{}}}`, 336 removed: _NS(), 337 modified: _NS(), 338 added: _NS(_P("a", "b")), 339 }, { 340 lhs: `{"a":{"b":{}}}`, 341 rhs: `{"a":{}}}`, 342 removed: _NS(_P("a", "b")), 343 modified: _NS(), 344 added: _NS(), 345 }, { 346 lhs: `{"a":{"b":{}}}`, 347 rhs: `{"a":null}`, 348 removed: _NS(_P("a", "b")), 349 modified: _NS(), 350 added: _NS(), 351 }, { 352 lhs: `{"a":[]}`, 353 rhs: `{"a":["b"]}`, 354 removed: _NS(), 355 modified: _NS(_P("a")), 356 added: _NS(), 357 }, { 358 lhs: `{"a":null}`, 359 rhs: `{"a":["b"]}`, 360 removed: _NS(), 361 modified: _NS(_P("a")), 362 added: _NS(), 363 }, { 364 lhs: `{"a":["b"]}`, 365 rhs: `{"a":[]}`, 366 removed: _NS(), 367 modified: _NS(_P("a")), 368 added: _NS(), 369 }, { 370 lhs: `{"a":["b"]}`, 371 rhs: `{"a":null}`, 372 removed: _NS(), 373 modified: _NS(_P("a")), 374 added: _NS(), 375 }, { 376 lhs: `{"a":null}`, 377 rhs: `{"a":"b"}`, 378 removed: _NS(), 379 modified: _NS(_P("a")), 380 added: _NS(), 381 }, { 382 lhs: `{"a":"b"}`, 383 rhs: `{"a":null}`, 384 removed: _NS(), 385 modified: _NS(_P("a")), 386 added: _NS(), 387 }, { 388 lhs: `{"a":{"b":{}}}`, 389 rhs: `{"a":["b"]}}`, 390 removed: _NS(_P("a", "b")), 391 modified: _NS(_P("a")), 392 added: _NS(), 393 }, { 394 lhs: `{"a":["b"]}}`, 395 rhs: `{"a":{"b":{}}}`, 396 removed: _NS(), 397 modified: _NS(_P("a")), 398 added: _NS(_P("a", "b")), 399 }, { 400 lhs: `{"a":{"b":{}}}`, 401 rhs: `{"a":"b"}`, 402 removed: _NS(_P("a", "b")), 403 modified: _NS(_P("a")), 404 added: _NS(), 405 }, { 406 lhs: `{"a":"b"}`, 407 rhs: `{"a":{"b":{}}}`, 408 removed: _NS(), 409 modified: _NS(_P("a")), 410 added: _NS(_P("a", "b")), 411 }, { 412 lhs: `{"a":["b"]}}`, 413 rhs: `{"a":"b"}`, 414 removed: _NS(), 415 modified: _NS(_P("a")), 416 added: _NS(), 417 }, { 418 lhs: `{"a":"b"}`, 419 rhs: `{"a":["b"]}}`, 420 removed: _NS(), 421 modified: _NS(_P("a")), 422 added: _NS(), 423 }}, 424}, { 425 name: "untyped separable", 426 rootTypeName: "__untyped_separable_", 427 schema: `types: 428- name: __untyped_separable_ 429 scalar: untyped 430 list: 431 elementType: 432 namedType: __untyped_separable_ 433 elementRelationship: associative 434 map: 435 elementType: 436 namedType: __untyped_separable_ 437 elementRelationship: separable 438`, 439 quints: []symdiffQuint{{ 440 lhs: `{"a":{}}}`, 441 rhs: `{"a":{"b":{}}}`, 442 removed: _NS(), 443 modified: _NS(), 444 added: _NS(_P("a", "b")), 445 }, { 446 lhs: `{"a":null}`, 447 rhs: `{"a":{"b":{}}}`, 448 removed: _NS(), 449 modified: _NS(), 450 added: _NS(_P("a", "b")), 451 }, { 452 lhs: `{"a":{"b":{}}}`, 453 rhs: `{"a":{}}}`, 454 removed: _NS(_P("a", "b")), 455 modified: _NS(), 456 added: _NS(), 457 }, { 458 lhs: `{"a":{"b":{}}}`, 459 rhs: `{"a":null}`, 460 removed: _NS(_P("a", "b")), 461 modified: _NS(), 462 added: _NS(), 463 }, { 464 lhs: `{"a":[]}`, 465 rhs: `{"a":["b"]}`, 466 removed: _NS(), 467 modified: _NS(), 468 added: _NS(_P("a", _V("b"))), 469 }, { 470 lhs: `{"a":null}`, 471 rhs: `{"a":["b"]}`, 472 removed: _NS(), 473 // TODO: result should be the same as the previous case 474 // nothing shoule be modified here. 475 modified: _NS(_P("a")), 476 added: _NS(_P("a", _V("b"))), 477 }, { 478 lhs: `{"a":["b"]}`, 479 rhs: `{"a":[]}`, 480 removed: _NS(_P("a", _V("b"))), 481 modified: _NS(), 482 added: _NS(), 483 }, { 484 lhs: `{"a":["b"]}`, 485 rhs: `{"a":null}`, 486 removed: _NS(_P("a", _V("b"))), 487 // TODO: result should be the same as the previous case 488 // nothing shoule be modified here. 489 modified: _NS(_P("a")), 490 added: _NS(), 491 }, { 492 lhs: `{"a":null}`, 493 rhs: `{"a":"b"}`, 494 removed: _NS(), 495 modified: _NS(_P("a")), 496 added: _NS(), 497 }, { 498 lhs: `{"a":"b"}`, 499 rhs: `{"a":null}`, 500 removed: _NS(), 501 modified: _NS(_P("a")), 502 added: _NS(), 503 }, { 504 lhs: `{"a":{"b":{}}}`, 505 rhs: `{"a":["b"]}}`, 506 removed: _NS(_P("a", "b")), 507 modified: _NS(), 508 added: _NS(_P("a", _V("b"))), 509 }, { 510 lhs: `{"a":["b"]}}`, 511 rhs: `{"a":{"b":{}}}`, 512 removed: _NS(_P("a", _V("b"))), 513 modified: _NS(), 514 added: _NS(_P("a", "b")), 515 }, { 516 lhs: `{"a":{"b":{}}}`, 517 rhs: `{"a":"b"}`, 518 removed: _NS(_P("a", "b")), 519 modified: _NS(_P("a")), 520 added: _NS(), 521 }, { 522 lhs: `{"a":"b"}`, 523 rhs: `{"a":{"b":{}}}`, 524 removed: _NS(), 525 modified: _NS(_P("a")), 526 added: _NS(_P("a", "b")), 527 }, { 528 lhs: `{"a":["b"]}}`, 529 rhs: `{"a":"b"}`, 530 removed: _NS(_P("a", _V("b"))), 531 modified: _NS(_P("a")), 532 added: _NS(), 533 }, { 534 lhs: `{"a":"b"}`, 535 rhs: `{"a":["b"]}}`, 536 removed: _NS(), 537 modified: _NS(_P("a")), 538 added: _NS(_P("a", _V("b"))), 539 }}, 540}, { 541 name: "struct grab bag", 542 rootTypeName: "myStruct", 543 schema: `types: 544- name: myStruct 545 map: 546 fields: 547 - name: numeric 548 type: 549 scalar: numeric 550 - name: string 551 type: 552 scalar: string 553 - name: bool 554 type: 555 scalar: boolean 556 - name: setStr 557 type: 558 list: 559 elementType: 560 scalar: string 561 elementRelationship: associative 562 - name: setBool 563 type: 564 list: 565 elementType: 566 scalar: boolean 567 elementRelationship: associative 568 - name: setNumeric 569 type: 570 list: 571 elementType: 572 scalar: numeric 573 elementRelationship: associative 574`, 575 quints: []symdiffQuint{{ 576 lhs: `{"numeric":1}`, 577 rhs: `{"numeric":3.14159}`, 578 removed: _NS(), 579 modified: _NS(_P("numeric")), 580 added: _NS(), 581 }, { 582 lhs: `{"numeric":3.14159}`, 583 rhs: `{"numeric":1}`, 584 removed: _NS(), 585 modified: _NS(_P("numeric")), 586 added: _NS(), 587 }, { 588 lhs: `{"string":"aoeu"}`, 589 rhs: `{"bool":true}`, 590 removed: _NS(_P("string")), 591 modified: _NS(), 592 added: _NS(_P("bool")), 593 }, { 594 lhs: `{"setStr":["a","b"]}`, 595 rhs: `{"setStr":["a","b","c"]}`, 596 removed: _NS(), 597 modified: _NS(), 598 added: _NS(_P("setStr", _V("c"))), 599 }, { 600 lhs: `{"setStr":["a","b","c"]}`, 601 rhs: `{"setStr":[]}`, 602 removed: _NS( 603 _P("setStr", _V("a")), 604 _P("setStr", _V("b")), 605 _P("setStr", _V("c")), 606 ), 607 modified: _NS(), 608 added: _NS(), 609 }, { 610 lhs: `{"setBool":[true]}`, 611 rhs: `{"setBool":[false]}`, 612 removed: _NS(_P("setBool", _V(true))), 613 modified: _NS(), 614 added: _NS(_P("setBool", _V(false))), 615 }, { 616 lhs: `{"setNumeric":[1,2,3.14159]}`, 617 rhs: `{"setNumeric":[1,2,3]}`, 618 removed: _NS(_P("setNumeric", _V(3.14159))), 619 modified: _NS(), 620 added: _NS(_P("setNumeric", _V(3))), 621 }}, 622}, { 623 name: "associative list", 624 rootTypeName: "myRoot", 625 schema: `types: 626- name: myRoot 627 map: 628 fields: 629 - name: list 630 type: 631 namedType: myList 632 - name: atomicList 633 type: 634 namedType: mySequence 635- name: myList 636 list: 637 elementType: 638 namedType: myElement 639 elementRelationship: associative 640 keys: 641 - key 642 - id 643- name: mySequence 644 list: 645 elementType: 646 scalar: string 647 elementRelationship: atomic 648- name: myElement 649 map: 650 fields: 651 - name: key 652 type: 653 scalar: string 654 - name: id 655 type: 656 scalar: numeric 657 - name: value 658 type: 659 namedType: myValue 660 - name: bv 661 type: 662 scalar: boolean 663 - name: nv 664 type: 665 scalar: numeric 666- name: myValue 667 map: 668 elementType: 669 scalar: string 670`, 671 quints: []symdiffQuint{{ 672 lhs: `{}`, 673 rhs: `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`, 674 removed: _NS(), 675 modified: _NS(), 676 added: _NS( 677 _P("list"), 678 _P("list", _KBF("key", "a", "id", 1)), 679 _P("list", _KBF("key", "a", "id", 1), "key"), 680 _P("list", _KBF("key", "a", "id", 1), "id"), 681 _P("list", _KBF("key", "a", "id", 1), "value"), 682 _P("list", _KBF("key", "a", "id", 1), "value", "a"), 683 ), 684 }, { 685 lhs: `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`, 686 rhs: `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`, 687 removed: _NS(), 688 modified: _NS(), 689 added: _NS(), 690 }, { 691 lhs: `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`, 692 rhs: `{"list":[{"key":"a","id":1,"value":{"a":"b"}}]}`, 693 removed: _NS(), 694 modified: _NS(_P("list", _KBF("key", "a", "id", 1), "value", "a")), 695 added: _NS(), 696 }, { 697 lhs: `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`, 698 rhs: `{"list":[{"key":"a","id":2,"value":{"a":"a"}}]}`, 699 removed: _NS( 700 _P("list", _KBF("key", "a", "id", 1)), 701 _P("list", _KBF("key", "a", "id", 1), "key"), 702 _P("list", _KBF("key", "a", "id", 1), "id"), 703 _P("list", _KBF("key", "a", "id", 1), "value"), 704 _P("list", _KBF("key", "a", "id", 1), "value", "a"), 705 ), 706 modified: _NS(), 707 added: _NS( 708 _P("list", _KBF("key", "a", "id", 2)), 709 _P("list", _KBF("key", "a", "id", 2), "key"), 710 _P("list", _KBF("key", "a", "id", 2), "id"), 711 _P("list", _KBF("key", "a", "id", 2), "value"), 712 _P("list", _KBF("key", "a", "id", 2), "value", "a"), 713 ), 714 }, { 715 lhs: `{"list":[{"key":"a","id":1},{"key":"b","id":1}]}`, 716 rhs: `{"list":[{"key":"a","id":1},{"key":"a","id":2}]}`, 717 removed: _NS( 718 _P("list", _KBF("key", "b", "id", 1)), 719 _P("list", _KBF("key", "b", "id", 1), "key"), 720 _P("list", _KBF("key", "b", "id", 1), "id"), 721 ), 722 modified: _NS(), 723 added: _NS( 724 _P("list", _KBF("key", "a", "id", 2)), 725 _P("list", _KBF("key", "a", "id", 2), "key"), 726 _P("list", _KBF("key", "a", "id", 2), "id"), 727 ), 728 }, { 729 lhs: `{"atomicList":["a","a","a"]}`, 730 rhs: `{"atomicList":null}`, 731 removed: _NS(), 732 modified: _NS(_P("atomicList")), 733 added: _NS(), 734 }, { 735 lhs: `{"atomicList":["a","b","c"]}`, 736 rhs: `{"atomicList":[]}`, 737 removed: _NS(), 738 modified: _NS(_P("atomicList")), 739 added: _NS(), 740 }, { 741 lhs: `{"atomicList":["a","a","a"]}`, 742 rhs: `{"atomicList":["a","a"]}`, 743 removed: _NS(), 744 modified: _NS(_P("atomicList")), 745 added: _NS(), 746 }}, 747}} 748 749func (tt symdiffTestCase) test(t *testing.T) { 750 parser, err := typed.NewParser(tt.schema) 751 if err != nil { 752 t.Fatalf("failed to create schema: %v", err) 753 } 754 for i, quint := range tt.quints { 755 quint := quint 756 t.Run(fmt.Sprintf("%v-valid-%v", tt.name, i), func(t *testing.T) { 757 t.Parallel() 758 pt := parser.Type(tt.rootTypeName) 759 760 tvLHS, err := pt.FromYAML(quint.lhs) 761 if err != nil { 762 t.Fatalf("failed to parse lhs: %v", err) 763 } 764 tvRHS, err := pt.FromYAML(quint.rhs) 765 if err != nil { 766 t.Fatalf("failed to parse rhs: %v", err) 767 } 768 got, err := tvLHS.Compare(tvRHS) 769 if err != nil { 770 t.Fatalf("got validation errors: %v", err) 771 } 772 t.Logf("got added:\n%s\n", got.Added) 773 if !got.Added.Equals(quint.added) { 774 t.Errorf("Expected added:\n%s\n", quint.added) 775 } 776 t.Logf("got modified:\n%s", got.Modified) 777 if !got.Modified.Equals(quint.modified) { 778 t.Errorf("Expected modified:\n%s\n", quint.modified) 779 } 780 t.Logf("got removed:\n%s", got.Removed) 781 if !got.Removed.Equals(quint.removed) { 782 t.Errorf("Expected removed:\n%s\n", quint.removed) 783 } 784 785 // Do the reverse operation and sanity check. 786 gotR, err := tvRHS.Compare(tvLHS) 787 if err != nil { 788 t.Fatalf("(reverse) got validation errors: %v", err) 789 } 790 if !gotR.Modified.Equals(got.Modified) { 791 t.Errorf("reverse operation gave different modified list:\n%s", gotR.Modified) 792 } 793 if !gotR.Removed.Equals(got.Added) { 794 t.Errorf("reverse removed gave different result than added:\n%s", gotR.Removed) 795 } 796 if !gotR.Added.Equals(got.Removed) { 797 t.Errorf("reverse added gave different result than removed:\n%s", gotR.Added) 798 } 799 800 }) 801 } 802} 803 804func TestSymdiff(t *testing.T) { 805 for _, tt := range symdiffCases { 806 tt := tt 807 t.Run(tt.name, func(t *testing.T) { 808 t.Parallel() 809 tt.test(t) 810 }) 811 } 812} 813