1 // Copyright 2015 Google Inc. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package proptools 16 17 import ( 18 "errors" 19 "fmt" 20 "reflect" 21 "strings" 22 "testing" 23 ) 24 25 var appendPropertiesTestCases = []struct { 26 in1 interface{} 27 in2 interface{} 28 out interface{} 29 prepend bool 30 filter ExtendPropertyFilterFunc 31 err error 32 }{ 33 // Valid inputs 34 35 { 36 // Append bool 37 in1: &struct{ B1, B2, B3, B4 bool }{ 38 B1: true, 39 B2: false, 40 B3: true, 41 B4: false, 42 }, 43 in2: &struct{ B1, B2, B3, B4 bool }{ 44 B1: true, 45 B2: true, 46 B3: false, 47 B4: false, 48 }, 49 out: &struct{ B1, B2, B3, B4 bool }{ 50 B1: true, 51 B2: true, 52 B3: true, 53 B4: false, 54 }, 55 }, 56 { 57 // Prepend bool 58 in1: &struct{ B1, B2, B3, B4 bool }{ 59 B1: true, 60 B2: false, 61 B3: true, 62 B4: false, 63 }, 64 in2: &struct{ B1, B2, B3, B4 bool }{ 65 B1: true, 66 B2: true, 67 B3: false, 68 B4: false, 69 }, 70 out: &struct{ B1, B2, B3, B4 bool }{ 71 B1: true, 72 B2: true, 73 B3: true, 74 B4: false, 75 }, 76 prepend: true, 77 }, 78 { 79 // Append strings 80 in1: &struct{ S string }{ 81 S: "string1", 82 }, 83 in2: &struct{ S string }{ 84 S: "string2", 85 }, 86 out: &struct{ S string }{ 87 S: "string1string2", 88 }, 89 }, 90 { 91 // Prepend strings 92 in1: &struct{ S string }{ 93 S: "string1", 94 }, 95 in2: &struct{ S string }{ 96 S: "string2", 97 }, 98 out: &struct{ S string }{ 99 S: "string2string1", 100 }, 101 prepend: true, 102 }, 103 { 104 // Append pointer to bool 105 in1: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ 106 B1: BoolPtr(true), 107 B2: BoolPtr(false), 108 B3: nil, 109 B4: BoolPtr(true), 110 B5: BoolPtr(false), 111 B6: nil, 112 B7: BoolPtr(true), 113 B8: BoolPtr(false), 114 B9: nil, 115 }, 116 in2: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ 117 B1: nil, 118 B2: nil, 119 B3: nil, 120 B4: BoolPtr(true), 121 B5: BoolPtr(true), 122 B6: BoolPtr(true), 123 B7: BoolPtr(false), 124 B8: BoolPtr(false), 125 B9: BoolPtr(false), 126 }, 127 out: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ 128 B1: BoolPtr(true), 129 B2: BoolPtr(false), 130 B3: nil, 131 B4: BoolPtr(true), 132 B5: BoolPtr(true), 133 B6: BoolPtr(true), 134 B7: BoolPtr(false), 135 B8: BoolPtr(false), 136 B9: BoolPtr(false), 137 }, 138 }, 139 { 140 // Prepend pointer to bool 141 in1: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ 142 B1: BoolPtr(true), 143 B2: BoolPtr(false), 144 B3: nil, 145 B4: BoolPtr(true), 146 B5: BoolPtr(false), 147 B6: nil, 148 B7: BoolPtr(true), 149 B8: BoolPtr(false), 150 B9: nil, 151 }, 152 in2: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ 153 B1: nil, 154 B2: nil, 155 B3: nil, 156 B4: BoolPtr(true), 157 B5: BoolPtr(true), 158 B6: BoolPtr(true), 159 B7: BoolPtr(false), 160 B8: BoolPtr(false), 161 B9: BoolPtr(false), 162 }, 163 out: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ 164 B1: BoolPtr(true), 165 B2: BoolPtr(false), 166 B3: nil, 167 B4: BoolPtr(true), 168 B5: BoolPtr(false), 169 B6: BoolPtr(true), 170 B7: BoolPtr(true), 171 B8: BoolPtr(false), 172 B9: BoolPtr(false), 173 }, 174 prepend: true, 175 }, 176 { 177 // Append pointer to strings 178 in1: &struct{ S1, S2, S3, S4 *string }{ 179 S1: StringPtr("string1"), 180 S2: StringPtr("string2"), 181 }, 182 in2: &struct{ S1, S2, S3, S4 *string }{ 183 S1: StringPtr("string3"), 184 S3: StringPtr("string4"), 185 }, 186 out: &struct{ S1, S2, S3, S4 *string }{ 187 S1: StringPtr("string3"), 188 S2: StringPtr("string2"), 189 S3: StringPtr("string4"), 190 S4: nil, 191 }, 192 }, 193 { 194 // Prepend pointer to strings 195 in1: &struct{ S1, S2, S3, S4 *string }{ 196 S1: StringPtr("string1"), 197 S2: StringPtr("string2"), 198 }, 199 in2: &struct{ S1, S2, S3, S4 *string }{ 200 S1: StringPtr("string3"), 201 S3: StringPtr("string4"), 202 }, 203 out: &struct{ S1, S2, S3, S4 *string }{ 204 S1: StringPtr("string1"), 205 S2: StringPtr("string2"), 206 S3: StringPtr("string4"), 207 S4: nil, 208 }, 209 prepend: true, 210 }, 211 { 212 // Append slice 213 in1: &struct{ S []string }{ 214 S: []string{"string1"}, 215 }, 216 in2: &struct{ S []string }{ 217 S: []string{"string2"}, 218 }, 219 out: &struct{ S []string }{ 220 S: []string{"string1", "string2"}, 221 }, 222 }, 223 { 224 // Prepend slice 225 in1: &struct{ S []string }{ 226 S: []string{"string1"}, 227 }, 228 in2: &struct{ S []string }{ 229 S: []string{"string2"}, 230 }, 231 out: &struct{ S []string }{ 232 S: []string{"string2", "string1"}, 233 }, 234 prepend: true, 235 }, 236 { 237 // Append empty slice 238 in1: &struct{ S1, S2 []string }{ 239 S1: []string{"string1"}, 240 S2: []string{}, 241 }, 242 in2: &struct{ S1, S2 []string }{ 243 S1: []string{}, 244 S2: []string{"string2"}, 245 }, 246 out: &struct{ S1, S2 []string }{ 247 S1: []string{"string1"}, 248 S2: []string{"string2"}, 249 }, 250 }, 251 { 252 // Prepend empty slice 253 in1: &struct{ S1, S2 []string }{ 254 S1: []string{"string1"}, 255 S2: []string{}, 256 }, 257 in2: &struct{ S1, S2 []string }{ 258 S1: []string{}, 259 S2: []string{"string2"}, 260 }, 261 out: &struct{ S1, S2 []string }{ 262 S1: []string{"string1"}, 263 S2: []string{"string2"}, 264 }, 265 prepend: true, 266 }, 267 { 268 // Append nil slice 269 in1: &struct{ S1, S2, S3 []string }{ 270 S1: []string{"string1"}, 271 }, 272 in2: &struct{ S1, S2, S3 []string }{ 273 S2: []string{"string2"}, 274 }, 275 out: &struct{ S1, S2, S3 []string }{ 276 S1: []string{"string1"}, 277 S2: []string{"string2"}, 278 S3: nil, 279 }, 280 }, 281 { 282 // Prepend nil slice 283 in1: &struct{ S1, S2, S3 []string }{ 284 S1: []string{"string1"}, 285 }, 286 in2: &struct{ S1, S2, S3 []string }{ 287 S2: []string{"string2"}, 288 }, 289 out: &struct{ S1, S2, S3 []string }{ 290 S1: []string{"string1"}, 291 S2: []string{"string2"}, 292 S3: nil, 293 }, 294 prepend: true, 295 }, 296 { 297 // Append pointer 298 in1: &struct{ S *struct{ S string } }{ 299 S: &struct{ S string }{ 300 S: "string1", 301 }, 302 }, 303 in2: &struct{ S *struct{ S string } }{ 304 S: &struct{ S string }{ 305 S: "string2", 306 }, 307 }, 308 out: &struct{ S *struct{ S string } }{ 309 S: &struct{ S string }{ 310 S: "string1string2", 311 }, 312 }, 313 }, 314 { 315 // Prepend pointer 316 in1: &struct{ S *struct{ S string } }{ 317 S: &struct{ S string }{ 318 S: "string1", 319 }, 320 }, 321 in2: &struct{ S *struct{ S string } }{ 322 S: &struct{ S string }{ 323 S: "string2", 324 }, 325 }, 326 out: &struct{ S *struct{ S string } }{ 327 S: &struct{ S string }{ 328 S: "string2string1", 329 }, 330 }, 331 prepend: true, 332 }, 333 { 334 // Append interface 335 in1: &struct{ S interface{} }{ 336 S: &struct{ S string }{ 337 S: "string1", 338 }, 339 }, 340 in2: &struct{ S interface{} }{ 341 S: &struct{ S string }{ 342 S: "string2", 343 }, 344 }, 345 out: &struct{ S interface{} }{ 346 S: &struct{ S string }{ 347 S: "string1string2", 348 }, 349 }, 350 }, 351 { 352 // Prepend interface 353 in1: &struct{ S interface{} }{ 354 S: &struct{ S string }{ 355 S: "string1", 356 }, 357 }, 358 in2: &struct{ S interface{} }{ 359 S: &struct{ S string }{ 360 S: "string2", 361 }, 362 }, 363 out: &struct{ S interface{} }{ 364 S: &struct{ S string }{ 365 S: "string2string1", 366 }, 367 }, 368 prepend: true, 369 }, 370 { 371 // Unexported field 372 in1: &struct{ s string }{ 373 s: "string1", 374 }, 375 in2: &struct{ s string }{ 376 s: "string2", 377 }, 378 out: &struct{ s string }{ 379 s: "string1", 380 }, 381 }, 382 { 383 // Empty struct 384 in1: &struct{}{}, 385 in2: &struct{}{}, 386 out: &struct{}{}, 387 }, 388 { 389 // Interface nil 390 in1: &struct{ S interface{} }{ 391 S: nil, 392 }, 393 in2: &struct{ S interface{} }{ 394 S: nil, 395 }, 396 out: &struct{ S interface{} }{ 397 S: nil, 398 }, 399 }, 400 { 401 // Pointer nil 402 in1: &struct{ S *struct{} }{ 403 S: nil, 404 }, 405 in2: &struct{ S *struct{} }{ 406 S: nil, 407 }, 408 out: &struct{ S *struct{} }{ 409 S: nil, 410 }, 411 }, 412 { 413 // Anonymous struct 414 in1: &struct { 415 EmbeddedStruct 416 Nested struct{ EmbeddedStruct } 417 }{ 418 EmbeddedStruct: EmbeddedStruct{ 419 S: "string1", 420 }, 421 Nested: struct{ EmbeddedStruct }{ 422 EmbeddedStruct: EmbeddedStruct{ 423 S: "string2", 424 }, 425 }, 426 }, 427 in2: &struct { 428 EmbeddedStruct 429 Nested struct{ EmbeddedStruct } 430 }{ 431 EmbeddedStruct: EmbeddedStruct{ 432 S: "string3", 433 }, 434 Nested: struct{ EmbeddedStruct }{ 435 EmbeddedStruct: EmbeddedStruct{ 436 S: "string4", 437 }, 438 }, 439 }, 440 out: &struct { 441 EmbeddedStruct 442 Nested struct{ EmbeddedStruct } 443 }{ 444 EmbeddedStruct: EmbeddedStruct{ 445 S: "string1string3", 446 }, 447 Nested: struct{ EmbeddedStruct }{ 448 EmbeddedStruct: EmbeddedStruct{ 449 S: "string2string4", 450 }, 451 }, 452 }, 453 }, 454 { 455 // Anonymous interface 456 in1: &struct { 457 EmbeddedInterface 458 Nested struct{ EmbeddedInterface } 459 }{ 460 EmbeddedInterface: &struct{ S string }{ 461 S: "string1", 462 }, 463 Nested: struct{ EmbeddedInterface }{ 464 EmbeddedInterface: &struct{ S string }{ 465 S: "string2", 466 }, 467 }, 468 }, 469 in2: &struct { 470 EmbeddedInterface 471 Nested struct{ EmbeddedInterface } 472 }{ 473 EmbeddedInterface: &struct{ S string }{ 474 S: "string3", 475 }, 476 Nested: struct{ EmbeddedInterface }{ 477 EmbeddedInterface: &struct{ S string }{ 478 S: "string4", 479 }, 480 }, 481 }, 482 out: &struct { 483 EmbeddedInterface 484 Nested struct{ EmbeddedInterface } 485 }{ 486 EmbeddedInterface: &struct{ S string }{ 487 S: "string1string3", 488 }, 489 Nested: struct{ EmbeddedInterface }{ 490 EmbeddedInterface: &struct{ S string }{ 491 S: "string2string4", 492 }, 493 }, 494 }, 495 }, 496 497 // Errors 498 499 { 500 // Non-pointer in1 501 in1: struct{}{}, 502 err: errors.New("expected pointer to struct, got struct {}"), 503 out: struct{}{}, 504 }, 505 { 506 // Non-pointer in2 507 in1: &struct{}{}, 508 in2: struct{}{}, 509 err: errors.New("expected pointer to struct, got struct {}"), 510 out: &struct{}{}, 511 }, 512 { 513 // Non-struct in1 514 in1: &[]string{"bad"}, 515 err: errors.New("expected pointer to struct, got *[]string"), 516 out: &[]string{"bad"}, 517 }, 518 { 519 // Non-struct in2 520 in1: &struct{}{}, 521 in2: &[]string{"bad"}, 522 err: errors.New("expected pointer to struct, got *[]string"), 523 out: &struct{}{}, 524 }, 525 { 526 // Mismatched types 527 in1: &struct{ A string }{ 528 A: "string1", 529 }, 530 in2: &struct{ B string }{ 531 B: "string2", 532 }, 533 out: &struct{ A string }{ 534 A: "string1", 535 }, 536 err: errors.New("expected matching types for dst and src, got *struct { A string } and *struct { B string }"), 537 }, 538 { 539 // Unsupported kind 540 in1: &struct{ I int }{ 541 I: 1, 542 }, 543 in2: &struct{ I int }{ 544 I: 2, 545 }, 546 out: &struct{ I int }{ 547 I: 1, 548 }, 549 err: extendPropertyErrorf("i", "unsupported kind int"), 550 }, 551 { 552 // Interface nilitude mismatch 553 in1: &struct{ S interface{} }{ 554 S: &struct{ S string }{ 555 S: "string1", 556 }, 557 }, 558 in2: &struct{ S interface{} }{ 559 S: nil, 560 }, 561 out: &struct{ S interface{} }{ 562 S: &struct{ S string }{ 563 S: "string1", 564 }, 565 }, 566 err: extendPropertyErrorf("s", "nilitude mismatch"), 567 }, 568 { 569 // Interface type mismatch 570 in1: &struct{ S interface{} }{ 571 S: &struct{ A string }{ 572 A: "string1", 573 }, 574 }, 575 in2: &struct{ S interface{} }{ 576 S: &struct{ B string }{ 577 B: "string2", 578 }, 579 }, 580 out: &struct{ S interface{} }{ 581 S: &struct{ A string }{ 582 A: "string1", 583 }, 584 }, 585 err: extendPropertyErrorf("s", "mismatched types struct { A string } and struct { B string }"), 586 }, 587 { 588 // Interface not a pointer 589 in1: &struct{ S interface{} }{ 590 S: struct{ S string }{ 591 S: "string1", 592 }, 593 }, 594 in2: &struct{ S interface{} }{ 595 S: struct{ S string }{ 596 S: "string2", 597 }, 598 }, 599 out: &struct{ S interface{} }{ 600 S: struct{ S string }{ 601 S: "string1", 602 }, 603 }, 604 err: extendPropertyErrorf("s", "interface not a pointer"), 605 }, 606 { 607 // Pointer nilitude mismatch 608 in1: &struct{ S *struct{ S string } }{ 609 S: &struct{ S string }{ 610 S: "string1", 611 }, 612 }, 613 in2: &struct{ S *struct{ S string } }{ 614 S: nil, 615 }, 616 out: &struct{ S *struct{ S string } }{ 617 S: &struct{ S string }{ 618 S: "string1", 619 }, 620 }, 621 err: extendPropertyErrorf("s", "nilitude mismatch"), 622 }, 623 { 624 // Pointer not a struct 625 in1: &struct{ S *[]string }{ 626 S: &[]string{"string1"}, 627 }, 628 in2: &struct{ S *[]string }{ 629 S: &[]string{"string2"}, 630 }, 631 out: &struct{ S *[]string }{ 632 S: &[]string{"string1"}, 633 }, 634 err: extendPropertyErrorf("s", "pointer is a slice"), 635 }, 636 { 637 // Error in nested struct 638 in1: &struct{ S interface{} }{ 639 S: &struct{ I int }{ 640 I: 1, 641 }, 642 }, 643 in2: &struct{ S interface{} }{ 644 S: &struct{ I int }{ 645 I: 2, 646 }, 647 }, 648 out: &struct{ S interface{} }{ 649 S: &struct{ I int }{ 650 I: 1, 651 }, 652 }, 653 err: extendPropertyErrorf("s.i", "unsupported kind int"), 654 }, 655 656 // Filters 657 658 { 659 // Filter true 660 in1: &struct{ S string }{ 661 S: "string1", 662 }, 663 in2: &struct{ S string }{ 664 S: "string2", 665 }, 666 out: &struct{ S string }{ 667 S: "string1string2", 668 }, 669 filter: func(property string, 670 dstField, srcField reflect.StructField, 671 dstValue, srcValue interface{}) (bool, error) { 672 return true, nil 673 }, 674 }, 675 { 676 // Filter false 677 in1: &struct{ S string }{ 678 S: "string1", 679 }, 680 in2: &struct{ S string }{ 681 S: "string2", 682 }, 683 out: &struct{ S string }{ 684 S: "string1", 685 }, 686 filter: func(property string, 687 dstField, srcField reflect.StructField, 688 dstValue, srcValue interface{}) (bool, error) { 689 return false, nil 690 }, 691 }, 692 { 693 // Filter check args 694 in1: &struct{ S string }{ 695 S: "string1", 696 }, 697 in2: &struct{ S string }{ 698 S: "string2", 699 }, 700 out: &struct{ S string }{ 701 S: "string1string2", 702 }, 703 filter: func(property string, 704 dstField, srcField reflect.StructField, 705 dstValue, srcValue interface{}) (bool, error) { 706 return property == "s" && 707 dstField.Name == "S" && srcField.Name == "S" && 708 dstValue.(string) == "string1" && srcValue.(string) == "string2", nil 709 }, 710 }, 711 { 712 // Filter mutated 713 in1: &struct { 714 S string `blueprint:"mutated"` 715 }{ 716 S: "string1", 717 }, 718 in2: &struct { 719 S string `blueprint:"mutated"` 720 }{ 721 S: "string2", 722 }, 723 out: &struct { 724 S string `blueprint:"mutated"` 725 }{ 726 S: "string1", 727 }, 728 }, 729 { 730 // Filter error 731 in1: &struct{ S string }{ 732 S: "string1", 733 }, 734 in2: &struct{ S string }{ 735 S: "string2", 736 }, 737 out: &struct{ S string }{ 738 S: "string1", 739 }, 740 filter: func(property string, 741 dstField, srcField reflect.StructField, 742 dstValue, srcValue interface{}) (bool, error) { 743 return true, fmt.Errorf("filter error") 744 }, 745 err: extendPropertyErrorf("s", "filter error"), 746 }, 747 } 748 749 func TestAppendProperties(t *testing.T) { 750 for _, testCase := range appendPropertiesTestCases { 751 testString := fmt.Sprintf("%v, %v -> %v", testCase.in1, testCase.in2, testCase.out) 752 753 got := testCase.in1 754 var err error 755 var testType string 756 757 if testCase.prepend { 758 testType = "prepend" 759 err = PrependProperties(got, testCase.in2, testCase.filter) 760 } else { 761 testType = "append" 762 err = AppendProperties(got, testCase.in2, testCase.filter) 763 } 764 765 check(t, testType, testString, got, err, testCase.out, testCase.err) 766 } 767 } 768 769 var appendMatchingPropertiesTestCases = []struct { 770 in1 []interface{} 771 in2 interface{} 772 out []interface{} 773 prepend bool 774 filter ExtendPropertyFilterFunc 775 err error 776 }{ 777 { 778 // Append strings 779 in1: []interface{}{&struct{ S string }{ 780 S: "string1", 781 }}, 782 in2: &struct{ S string }{ 783 S: "string2", 784 }, 785 out: []interface{}{&struct{ S string }{ 786 S: "string1string2", 787 }}, 788 }, 789 { 790 // Prepend strings 791 in1: []interface{}{&struct{ S string }{ 792 S: "string1", 793 }}, 794 in2: &struct{ S string }{ 795 S: "string2", 796 }, 797 out: []interface{}{&struct{ S string }{ 798 S: "string2string1", 799 }}, 800 prepend: true, 801 }, 802 { 803 // Append all 804 in1: []interface{}{ 805 &struct{ S, A string }{ 806 S: "string1", 807 }, 808 &struct{ S, B string }{ 809 S: "string2", 810 }, 811 }, 812 in2: &struct{ S string }{ 813 S: "string3", 814 }, 815 out: []interface{}{ 816 &struct{ S, A string }{ 817 S: "string1string3", 818 }, 819 &struct{ S, B string }{ 820 S: "string2string3", 821 }, 822 }, 823 }, 824 { 825 // Append some 826 in1: []interface{}{ 827 &struct{ S, A string }{ 828 S: "string1", 829 }, 830 &struct{ B string }{}, 831 }, 832 in2: &struct{ S string }{ 833 S: "string2", 834 }, 835 out: []interface{}{ 836 &struct{ S, A string }{ 837 S: "string1string2", 838 }, 839 &struct{ B string }{}, 840 }, 841 }, 842 { 843 // Append mismatched structs 844 in1: []interface{}{&struct{ S, A string }{ 845 S: "string1", 846 }}, 847 in2: &struct{ S string }{ 848 S: "string2", 849 }, 850 out: []interface{}{&struct{ S, A string }{ 851 S: "string1string2", 852 }}, 853 }, 854 { 855 // Append mismatched pointer structs 856 in1: []interface{}{&struct{ S *struct{ S, A string } }{ 857 S: &struct{ S, A string }{ 858 S: "string1", 859 }, 860 }}, 861 in2: &struct{ S *struct{ S string } }{ 862 S: &struct{ S string }{ 863 S: "string2", 864 }, 865 }, 866 out: []interface{}{&struct{ S *struct{ S, A string } }{ 867 S: &struct{ S, A string }{ 868 S: "string1string2", 869 }, 870 }}, 871 }, 872 873 // Errors 874 875 { 876 // Non-pointer in1 877 in1: []interface{}{struct{}{}}, 878 err: errors.New("expected pointer to struct, got struct {}"), 879 out: []interface{}{struct{}{}}, 880 }, 881 { 882 // Non-pointer in2 883 in1: []interface{}{&struct{}{}}, 884 in2: struct{}{}, 885 err: errors.New("expected pointer to struct, got struct {}"), 886 out: []interface{}{&struct{}{}}, 887 }, 888 { 889 // Non-struct in1 890 in1: []interface{}{&[]string{"bad"}}, 891 err: errors.New("expected pointer to struct, got *[]string"), 892 out: []interface{}{&[]string{"bad"}}, 893 }, 894 { 895 // Non-struct in2 896 in1: []interface{}{&struct{}{}}, 897 in2: &[]string{"bad"}, 898 err: errors.New("expected pointer to struct, got *[]string"), 899 out: []interface{}{&struct{}{}}, 900 }, 901 { 902 // Append none 903 in1: []interface{}{ 904 &struct{ A string }{}, 905 &struct{ B string }{}, 906 }, 907 in2: &struct{ S string }{ 908 S: "string1", 909 }, 910 out: []interface{}{ 911 &struct{ A string }{}, 912 &struct{ B string }{}, 913 }, 914 err: extendPropertyErrorf("s", "failed to find property to extend"), 915 }, 916 { 917 // Append mismatched kinds 918 in1: []interface{}{ 919 &struct{ S string }{ 920 S: "string1", 921 }, 922 }, 923 in2: &struct{ S []string }{ 924 S: []string{"string2"}, 925 }, 926 out: []interface{}{ 927 &struct{ S string }{ 928 S: "string1", 929 }, 930 }, 931 err: extendPropertyErrorf("s", "mismatched types string and []string"), 932 }, 933 { 934 // Append mismatched types 935 in1: []interface{}{ 936 &struct{ S []int }{ 937 S: []int{1}, 938 }, 939 }, 940 in2: &struct{ S []string }{ 941 S: []string{"string2"}, 942 }, 943 out: []interface{}{ 944 &struct{ S []int }{ 945 S: []int{1}, 946 }, 947 }, 948 err: extendPropertyErrorf("s", "mismatched types []int and []string"), 949 }, 950 } 951 952 func TestAppendMatchingProperties(t *testing.T) { 953 for _, testCase := range appendMatchingPropertiesTestCases { 954 testString := fmt.Sprintf("%s, %s -> %s", p(testCase.in1), p(testCase.in2), p(testCase.out)) 955 956 got := testCase.in1 957 var err error 958 var testType string 959 960 if testCase.prepend { 961 testType = "prepend matching" 962 err = PrependMatchingProperties(got, testCase.in2, testCase.filter) 963 } else { 964 testType = "append matching" 965 err = AppendMatchingProperties(got, testCase.in2, testCase.filter) 966 } 967 968 check(t, testType, testString, got, err, testCase.out, testCase.err) 969 } 970 } 971 972 func check(t *testing.T, testType, testString string, 973 got interface{}, err error, 974 expected interface{}, expectedErr error) { 975 976 printedTestCase := false 977 e := func(s string, expected, got interface{}) { 978 if !printedTestCase { 979 t.Errorf("test case %s: %s", testType, testString) 980 printedTestCase = true 981 } 982 t.Errorf("incorrect %s", s) 983 t.Errorf(" expected: %s", p(expected)) 984 t.Errorf(" got: %s", p(got)) 985 } 986 987 if err != nil { 988 if expectedErr != nil { 989 if err.Error() != expectedErr.Error() { 990 e("unexpected error", expectedErr.Error(), err.Error()) 991 } 992 } else { 993 e("unexpected error", nil, err.Error()) 994 } 995 } else { 996 if expectedErr != nil { 997 e("missing error", expectedErr, nil) 998 } 999 } 1000 1001 if !reflect.DeepEqual(expected, got) { 1002 e("output:", expected, got) 1003 } 1004 } 1005 1006 func p(in interface{}) string { 1007 if v, ok := in.([]interface{}); ok { 1008 s := make([]string, len(v)) 1009 for i := range v { 1010 s[i] = fmt.Sprintf("%#v", v[i]) 1011 } 1012 return "[" + strings.Join(s, ", ") + "]" 1013 } else { 1014 return fmt.Sprintf("%#v", in) 1015 } 1016 } 1017