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 15package proptools 16 17import ( 18 "errors" 19 "fmt" 20 "reflect" 21 "strings" 22 "testing" 23) 24 25type appendPropertyTestCase struct { 26 in1 interface{} 27 in2 interface{} 28 out interface{} 29 prepend bool 30 filter ExtendPropertyFilterFunc 31 err error 32} 33 34func appendPropertiesTestCases() []appendPropertyTestCase { 35 return []appendPropertyTestCase{ 36 // Valid inputs 37 38 { 39 // Append bool 40 in1: &struct{ B1, B2, B3, B4 bool }{ 41 B1: true, 42 B2: false, 43 B3: true, 44 B4: false, 45 }, 46 in2: &struct{ B1, B2, B3, B4 bool }{ 47 B1: true, 48 B2: true, 49 B3: false, 50 B4: false, 51 }, 52 out: &struct{ B1, B2, B3, B4 bool }{ 53 B1: true, 54 B2: true, 55 B3: true, 56 B4: false, 57 }, 58 }, 59 { 60 // Prepend bool 61 in1: &struct{ B1, B2, B3, B4 bool }{ 62 B1: true, 63 B2: false, 64 B3: true, 65 B4: false, 66 }, 67 in2: &struct{ B1, B2, B3, B4 bool }{ 68 B1: true, 69 B2: true, 70 B3: false, 71 B4: false, 72 }, 73 out: &struct{ B1, B2, B3, B4 bool }{ 74 B1: true, 75 B2: true, 76 B3: true, 77 B4: false, 78 }, 79 prepend: true, 80 }, 81 { 82 // Append strings 83 in1: &struct{ S string }{ 84 S: "string1", 85 }, 86 in2: &struct{ S string }{ 87 S: "string2", 88 }, 89 out: &struct{ S string }{ 90 S: "string1string2", 91 }, 92 }, 93 { 94 // Prepend strings 95 in1: &struct{ S string }{ 96 S: "string1", 97 }, 98 in2: &struct{ S string }{ 99 S: "string2", 100 }, 101 out: &struct{ S string }{ 102 S: "string2string1", 103 }, 104 prepend: true, 105 }, 106 { 107 // Append pointer to bool 108 in1: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ 109 B1: BoolPtr(true), 110 B2: BoolPtr(false), 111 B3: nil, 112 B4: BoolPtr(true), 113 B5: BoolPtr(false), 114 B6: nil, 115 B7: BoolPtr(true), 116 B8: BoolPtr(false), 117 B9: nil, 118 }, 119 in2: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ 120 B1: nil, 121 B2: nil, 122 B3: nil, 123 B4: BoolPtr(true), 124 B5: BoolPtr(true), 125 B6: BoolPtr(true), 126 B7: BoolPtr(false), 127 B8: BoolPtr(false), 128 B9: BoolPtr(false), 129 }, 130 out: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ 131 B1: BoolPtr(true), 132 B2: BoolPtr(false), 133 B3: nil, 134 B4: BoolPtr(true), 135 B5: BoolPtr(true), 136 B6: BoolPtr(true), 137 B7: BoolPtr(false), 138 B8: BoolPtr(false), 139 B9: BoolPtr(false), 140 }, 141 }, 142 { 143 // Prepend pointer to bool 144 in1: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ 145 B1: BoolPtr(true), 146 B2: BoolPtr(false), 147 B3: nil, 148 B4: BoolPtr(true), 149 B5: BoolPtr(false), 150 B6: nil, 151 B7: BoolPtr(true), 152 B8: BoolPtr(false), 153 B9: nil, 154 }, 155 in2: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ 156 B1: nil, 157 B2: nil, 158 B3: nil, 159 B4: BoolPtr(true), 160 B5: BoolPtr(true), 161 B6: BoolPtr(true), 162 B7: BoolPtr(false), 163 B8: BoolPtr(false), 164 B9: BoolPtr(false), 165 }, 166 out: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ 167 B1: BoolPtr(true), 168 B2: BoolPtr(false), 169 B3: nil, 170 B4: BoolPtr(true), 171 B5: BoolPtr(false), 172 B6: BoolPtr(true), 173 B7: BoolPtr(true), 174 B8: BoolPtr(false), 175 B9: BoolPtr(false), 176 }, 177 prepend: true, 178 }, 179 { 180 // Append pointer to integer 181 in1: &struct{ I1, I2, I3, I4, I5, I6, I7, I8, I9 *int64 }{ 182 I1: Int64Ptr(55), 183 I2: Int64Ptr(-3), 184 I3: nil, 185 I4: Int64Ptr(100), 186 I5: Int64Ptr(33), 187 I6: nil, 188 I7: Int64Ptr(77), 189 I8: Int64Ptr(0), 190 I9: nil, 191 }, 192 in2: &struct{ I1, I2, I3, I4, I5, I6, I7, I8, I9 *int64 }{ 193 I1: nil, 194 I2: nil, 195 I3: nil, 196 I4: Int64Ptr(1), 197 I5: Int64Ptr(-2), 198 I6: Int64Ptr(8), 199 I7: Int64Ptr(9), 200 I8: Int64Ptr(10), 201 I9: Int64Ptr(11), 202 }, 203 out: &struct{ I1, I2, I3, I4, I5, I6, I7, I8, I9 *int64 }{ 204 I1: Int64Ptr(55), 205 I2: Int64Ptr(-3), 206 I3: nil, 207 I4: Int64Ptr(1), 208 I5: Int64Ptr(-2), 209 I6: Int64Ptr(8), 210 I7: Int64Ptr(9), 211 I8: Int64Ptr(10), 212 I9: Int64Ptr(11), 213 }, 214 }, 215 { 216 // Prepend pointer to integer 217 in1: &struct{ I1, I2, I3 *int64 }{ 218 I1: Int64Ptr(55), 219 I3: nil, 220 }, 221 in2: &struct{ I1, I2, I3 *int64 }{ 222 I2: Int64Ptr(33), 223 }, 224 out: &struct{ I1, I2, I3 *int64 }{ 225 I1: Int64Ptr(55), 226 I2: Int64Ptr(33), 227 I3: nil, 228 }, 229 prepend: true, 230 }, 231 { 232 // Append pointer to strings 233 in1: &struct{ S1, S2, S3, S4 *string }{ 234 S1: StringPtr("string1"), 235 S2: StringPtr("string2"), 236 }, 237 in2: &struct{ S1, S2, S3, S4 *string }{ 238 S1: StringPtr("string3"), 239 S3: StringPtr("string4"), 240 }, 241 out: &struct{ S1, S2, S3, S4 *string }{ 242 S1: StringPtr("string3"), 243 S2: StringPtr("string2"), 244 S3: StringPtr("string4"), 245 S4: nil, 246 }, 247 }, 248 { 249 // Prepend pointer to strings 250 in1: &struct{ S1, S2, S3, S4 *string }{ 251 S1: StringPtr("string1"), 252 S2: StringPtr("string2"), 253 }, 254 in2: &struct{ S1, S2, S3, S4 *string }{ 255 S1: StringPtr("string3"), 256 S3: StringPtr("string4"), 257 }, 258 out: &struct{ S1, S2, S3, S4 *string }{ 259 S1: StringPtr("string1"), 260 S2: StringPtr("string2"), 261 S3: StringPtr("string4"), 262 S4: nil, 263 }, 264 prepend: true, 265 }, 266 { 267 // Append slice 268 in1: &struct{ S []string }{ 269 S: []string{"string1"}, 270 }, 271 in2: &struct{ S []string }{ 272 S: []string{"string2"}, 273 }, 274 out: &struct{ S []string }{ 275 S: []string{"string1", "string2"}, 276 }, 277 }, 278 { 279 // Prepend slice 280 in1: &struct{ S []string }{ 281 S: []string{"string1"}, 282 }, 283 in2: &struct{ S []string }{ 284 S: []string{"string2"}, 285 }, 286 out: &struct{ S []string }{ 287 S: []string{"string2", "string1"}, 288 }, 289 prepend: true, 290 }, 291 { 292 // Append empty slice 293 in1: &struct{ S1, S2 []string }{ 294 S1: []string{"string1"}, 295 S2: []string{}, 296 }, 297 in2: &struct{ S1, S2 []string }{ 298 S1: []string{}, 299 S2: []string{"string2"}, 300 }, 301 out: &struct{ S1, S2 []string }{ 302 S1: []string{"string1"}, 303 S2: []string{"string2"}, 304 }, 305 }, 306 { 307 // Prepend empty slice 308 in1: &struct{ S1, S2 []string }{ 309 S1: []string{"string1"}, 310 S2: []string{}, 311 }, 312 in2: &struct{ S1, S2 []string }{ 313 S1: []string{}, 314 S2: []string{"string2"}, 315 }, 316 out: &struct{ S1, S2 []string }{ 317 S1: []string{"string1"}, 318 S2: []string{"string2"}, 319 }, 320 prepend: true, 321 }, 322 { 323 // Append nil slice 324 in1: &struct{ S1, S2, S3 []string }{ 325 S1: []string{"string1"}, 326 }, 327 in2: &struct{ S1, S2, S3 []string }{ 328 S2: []string{"string2"}, 329 }, 330 out: &struct{ S1, S2, S3 []string }{ 331 S1: []string{"string1"}, 332 S2: []string{"string2"}, 333 S3: nil, 334 }, 335 }, 336 { 337 // Prepend nil slice 338 in1: &struct{ S1, S2, S3 []string }{ 339 S1: []string{"string1"}, 340 }, 341 in2: &struct{ S1, S2, S3 []string }{ 342 S2: []string{"string2"}, 343 }, 344 out: &struct{ S1, S2, S3 []string }{ 345 S1: []string{"string1"}, 346 S2: []string{"string2"}, 347 S3: nil, 348 }, 349 prepend: true, 350 }, 351 { 352 // Append pointer 353 in1: &struct{ S *struct{ S string } }{ 354 S: &struct{ S string }{ 355 S: "string1", 356 }, 357 }, 358 in2: &struct{ S *struct{ S string } }{ 359 S: &struct{ S string }{ 360 S: "string2", 361 }, 362 }, 363 out: &struct{ S *struct{ S string } }{ 364 S: &struct{ S string }{ 365 S: "string1string2", 366 }, 367 }, 368 }, 369 { 370 // Prepend pointer 371 in1: &struct{ S *struct{ S string } }{ 372 S: &struct{ S string }{ 373 S: "string1", 374 }, 375 }, 376 in2: &struct{ S *struct{ S string } }{ 377 S: &struct{ S string }{ 378 S: "string2", 379 }, 380 }, 381 out: &struct{ S *struct{ S string } }{ 382 S: &struct{ S string }{ 383 S: "string2string1", 384 }, 385 }, 386 prepend: true, 387 }, 388 { 389 // Append interface 390 in1: &struct{ S interface{} }{ 391 S: &struct{ S string }{ 392 S: "string1", 393 }, 394 }, 395 in2: &struct{ S interface{} }{ 396 S: &struct{ S string }{ 397 S: "string2", 398 }, 399 }, 400 out: &struct{ S interface{} }{ 401 S: &struct{ S string }{ 402 S: "string1string2", 403 }, 404 }, 405 }, 406 { 407 // Prepend interface 408 in1: &struct{ S interface{} }{ 409 S: &struct{ S string }{ 410 S: "string1", 411 }, 412 }, 413 in2: &struct{ S interface{} }{ 414 S: &struct{ S string }{ 415 S: "string2", 416 }, 417 }, 418 out: &struct{ S interface{} }{ 419 S: &struct{ S string }{ 420 S: "string2string1", 421 }, 422 }, 423 prepend: true, 424 }, 425 { 426 // Unexported field 427 in1: &struct{ s string }{ 428 s: "string1", 429 }, 430 in2: &struct{ s string }{ 431 s: "string2", 432 }, 433 out: &struct{ s string }{ 434 s: "string1", 435 }, 436 }, 437 { 438 // Unexported field 439 in1: &struct{ i *int64 }{ 440 i: Int64Ptr(33), 441 }, 442 in2: &struct{ i *int64 }{ 443 i: Int64Ptr(5), 444 }, 445 out: &struct{ i *int64 }{ 446 i: Int64Ptr(33), 447 }, 448 }, 449 { 450 // Empty struct 451 in1: &struct{}{}, 452 in2: &struct{}{}, 453 out: &struct{}{}, 454 }, 455 { 456 // Interface nil 457 in1: &struct{ S interface{} }{ 458 S: nil, 459 }, 460 in2: &struct{ S interface{} }{ 461 S: nil, 462 }, 463 out: &struct{ S interface{} }{ 464 S: nil, 465 }, 466 }, 467 { 468 // Pointer nil 469 in1: &struct{ S *struct{} }{ 470 S: nil, 471 }, 472 in2: &struct{ S *struct{} }{ 473 S: nil, 474 }, 475 out: &struct{ S *struct{} }{ 476 S: nil, 477 }, 478 }, 479 { 480 // Anonymous struct 481 in1: &struct { 482 EmbeddedStruct 483 Nested struct{ EmbeddedStruct } 484 }{ 485 EmbeddedStruct: EmbeddedStruct{ 486 S: "string1", 487 I: Int64Ptr(55), 488 }, 489 Nested: struct{ EmbeddedStruct }{ 490 EmbeddedStruct: EmbeddedStruct{ 491 S: "string2", 492 I: Int64Ptr(-4), 493 }, 494 }, 495 }, 496 in2: &struct { 497 EmbeddedStruct 498 Nested struct{ EmbeddedStruct } 499 }{ 500 EmbeddedStruct: EmbeddedStruct{ 501 S: "string3", 502 I: Int64Ptr(66), 503 }, 504 Nested: struct{ EmbeddedStruct }{ 505 EmbeddedStruct: EmbeddedStruct{ 506 S: "string4", 507 I: Int64Ptr(-8), 508 }, 509 }, 510 }, 511 out: &struct { 512 EmbeddedStruct 513 Nested struct{ EmbeddedStruct } 514 }{ 515 EmbeddedStruct: EmbeddedStruct{ 516 S: "string1string3", 517 I: Int64Ptr(66), 518 }, 519 Nested: struct{ EmbeddedStruct }{ 520 EmbeddedStruct: EmbeddedStruct{ 521 S: "string2string4", 522 I: Int64Ptr(-8), 523 }, 524 }, 525 }, 526 }, 527 { 528 // Anonymous interface 529 in1: &struct { 530 EmbeddedInterface 531 Nested struct{ EmbeddedInterface } 532 }{ 533 EmbeddedInterface: &struct { 534 S string 535 I *int64 536 }{ 537 S: "string1", 538 I: Int64Ptr(-8), 539 }, 540 Nested: struct{ EmbeddedInterface }{ 541 EmbeddedInterface: &struct { 542 S string 543 I *int64 544 }{ 545 S: "string2", 546 I: Int64Ptr(55), 547 }, 548 }, 549 }, 550 in2: &struct { 551 EmbeddedInterface 552 Nested struct{ EmbeddedInterface } 553 }{ 554 EmbeddedInterface: &struct { 555 S string 556 I *int64 557 }{ 558 S: "string3", 559 I: Int64Ptr(6), 560 }, 561 Nested: struct{ EmbeddedInterface }{ 562 EmbeddedInterface: &struct { 563 S string 564 I *int64 565 }{ 566 S: "string4", 567 I: Int64Ptr(6), 568 }, 569 }, 570 }, 571 out: &struct { 572 EmbeddedInterface 573 Nested struct{ EmbeddedInterface } 574 }{ 575 EmbeddedInterface: &struct { 576 S string 577 I *int64 578 }{ 579 S: "string1string3", 580 I: Int64Ptr(6), 581 }, 582 Nested: struct{ EmbeddedInterface }{ 583 EmbeddedInterface: &struct { 584 S string 585 I *int64 586 }{ 587 S: "string2string4", 588 I: Int64Ptr(6), 589 }, 590 }, 591 }, 592 }, 593 { 594 // Nil pointer to a struct 595 in1: &struct { 596 Nested *struct { 597 S string 598 } 599 }{}, 600 in2: &struct { 601 Nested *struct { 602 S string 603 } 604 }{ 605 Nested: &struct { 606 S string 607 }{ 608 S: "string", 609 }, 610 }, 611 out: &struct { 612 Nested *struct { 613 S string 614 } 615 }{ 616 Nested: &struct { 617 S string 618 }{ 619 S: "string", 620 }, 621 }, 622 }, 623 { 624 // Nil pointer to a struct in an interface 625 in1: &struct { 626 Nested interface{} 627 }{ 628 Nested: (*struct{ S string })(nil), 629 }, 630 in2: &struct { 631 Nested interface{} 632 }{ 633 Nested: &struct { 634 S string 635 }{ 636 S: "string", 637 }, 638 }, 639 out: &struct { 640 Nested interface{} 641 }{ 642 Nested: &struct { 643 S string 644 }{ 645 S: "string", 646 }, 647 }, 648 }, 649 { 650 // Interface src nil 651 in1: &struct{ S interface{} }{ 652 S: &struct{ S string }{ 653 S: "string1", 654 }, 655 }, 656 in2: &struct{ S interface{} }{ 657 S: nil, 658 }, 659 out: &struct{ S interface{} }{ 660 S: &struct{ S string }{ 661 S: "string1", 662 }, 663 }, 664 }, 665 666 // Errors 667 668 { 669 // Non-pointer in1 670 in1: struct{}{}, 671 in2: &struct{}{}, 672 err: errors.New("expected pointer to struct, got struct {}"), 673 out: struct{}{}, 674 }, 675 { 676 // Non-pointer in2 677 in1: &struct{}{}, 678 in2: struct{}{}, 679 err: errors.New("expected pointer to struct, got struct {}"), 680 out: &struct{}{}, 681 }, 682 { 683 // Non-struct in1 684 in1: &[]string{"bad"}, 685 in2: &struct{}{}, 686 err: errors.New("expected pointer to struct, got *[]string"), 687 out: &[]string{"bad"}, 688 }, 689 { 690 // Non-struct in2 691 in1: &struct{}{}, 692 in2: &[]string{"bad"}, 693 err: errors.New("expected pointer to struct, got *[]string"), 694 out: &struct{}{}, 695 }, 696 { 697 // Mismatched types 698 in1: &struct{ A string }{ 699 A: "string1", 700 }, 701 in2: &struct{ B string }{ 702 B: "string2", 703 }, 704 out: &struct{ A string }{ 705 A: "string1", 706 }, 707 err: errors.New("expected matching types for dst and src, got *struct { A string } and *struct { B string }"), 708 }, 709 { 710 // Unsupported kind 711 in1: &struct{ I int }{ 712 I: 1, 713 }, 714 in2: &struct{ I int }{ 715 I: 2, 716 }, 717 out: &struct{ I int }{ 718 I: 1, 719 }, 720 err: extendPropertyErrorf("i", "unsupported kind int"), 721 }, 722 { 723 // Unsupported kind 724 in1: &struct{ I int64 }{ 725 I: 1, 726 }, 727 in2: &struct{ I int64 }{ 728 I: 2, 729 }, 730 out: &struct{ I int64 }{ 731 I: 1, 732 }, 733 err: extendPropertyErrorf("i", "unsupported kind int64"), 734 }, 735 { 736 // Interface nilitude mismatch 737 in1: &struct{ S interface{} }{ 738 S: nil, 739 }, 740 in2: &struct{ S interface{} }{ 741 S: &struct{ S string }{ 742 S: "string1", 743 }, 744 }, 745 out: &struct{ S interface{} }{ 746 S: nil, 747 }, 748 err: extendPropertyErrorf("s", "nilitude mismatch"), 749 }, 750 { 751 // Interface type mismatch 752 in1: &struct{ S interface{} }{ 753 S: &struct{ A string }{ 754 A: "string1", 755 }, 756 }, 757 in2: &struct{ S interface{} }{ 758 S: &struct{ B string }{ 759 B: "string2", 760 }, 761 }, 762 out: &struct{ S interface{} }{ 763 S: &struct{ A string }{ 764 A: "string1", 765 }, 766 }, 767 err: extendPropertyErrorf("s", "mismatched types struct { A string } and struct { B string }"), 768 }, 769 { 770 // Interface not a pointer 771 in1: &struct{ S interface{} }{ 772 S: struct{ S string }{ 773 S: "string1", 774 }, 775 }, 776 in2: &struct{ S interface{} }{ 777 S: struct{ S string }{ 778 S: "string2", 779 }, 780 }, 781 out: &struct{ S interface{} }{ 782 S: struct{ S string }{ 783 S: "string1", 784 }, 785 }, 786 err: extendPropertyErrorf("s", "interface not a pointer"), 787 }, 788 { 789 // Pointer not a struct 790 in1: &struct{ S *[]string }{ 791 S: &[]string{"string1"}, 792 }, 793 in2: &struct{ S *[]string }{ 794 S: &[]string{"string2"}, 795 }, 796 out: &struct{ S *[]string }{ 797 S: &[]string{"string1"}, 798 }, 799 err: extendPropertyErrorf("s", "pointer is a slice"), 800 }, 801 { 802 // Error in nested struct 803 in1: &struct{ S interface{} }{ 804 S: &struct{ I int }{ 805 I: 1, 806 }, 807 }, 808 in2: &struct{ S interface{} }{ 809 S: &struct{ I int }{ 810 I: 2, 811 }, 812 }, 813 out: &struct{ S interface{} }{ 814 S: &struct{ I int }{ 815 I: 1, 816 }, 817 }, 818 err: extendPropertyErrorf("s.i", "unsupported kind int"), 819 }, 820 821 // Filters 822 823 { 824 // Filter true 825 in1: &struct{ S string }{ 826 S: "string1", 827 }, 828 in2: &struct{ S string }{ 829 S: "string2", 830 }, 831 out: &struct{ S string }{ 832 S: "string1string2", 833 }, 834 filter: func(property string, 835 dstField, srcField reflect.StructField, 836 dstValue, srcValue interface{}) (bool, error) { 837 return true, nil 838 }, 839 }, 840 { 841 // Filter false 842 in1: &struct{ S string }{ 843 S: "string1", 844 }, 845 in2: &struct{ S string }{ 846 S: "string2", 847 }, 848 out: &struct{ S string }{ 849 S: "string1", 850 }, 851 filter: func(property string, 852 dstField, srcField reflect.StructField, 853 dstValue, srcValue interface{}) (bool, error) { 854 return false, nil 855 }, 856 }, 857 { 858 // Filter check args 859 in1: &struct{ S string }{ 860 S: "string1", 861 }, 862 in2: &struct{ S string }{ 863 S: "string2", 864 }, 865 out: &struct{ S string }{ 866 S: "string1string2", 867 }, 868 filter: func(property string, 869 dstField, srcField reflect.StructField, 870 dstValue, srcValue interface{}) (bool, error) { 871 return property == "s" && 872 dstField.Name == "S" && srcField.Name == "S" && 873 dstValue.(string) == "string1" && srcValue.(string) == "string2", nil 874 }, 875 }, 876 { 877 // Filter mutated 878 in1: &struct { 879 S string `blueprint:"mutated"` 880 }{ 881 S: "string1", 882 }, 883 in2: &struct { 884 S string `blueprint:"mutated"` 885 }{ 886 S: "string2", 887 }, 888 out: &struct { 889 S string `blueprint:"mutated"` 890 }{ 891 S: "string1", 892 }, 893 }, 894 { 895 // Filter mutated 896 in1: &struct { 897 S *int64 `blueprint:"mutated"` 898 }{ 899 S: Int64Ptr(4), 900 }, 901 in2: &struct { 902 S *int64 `blueprint:"mutated"` 903 }{ 904 S: Int64Ptr(5), 905 }, 906 out: &struct { 907 S *int64 `blueprint:"mutated"` 908 }{ 909 S: Int64Ptr(4), 910 }, 911 }, 912 { 913 // Filter error 914 in1: &struct{ S string }{ 915 S: "string1", 916 }, 917 in2: &struct{ S string }{ 918 S: "string2", 919 }, 920 out: &struct{ S string }{ 921 S: "string1", 922 }, 923 filter: func(property string, 924 dstField, srcField reflect.StructField, 925 dstValue, srcValue interface{}) (bool, error) { 926 return true, fmt.Errorf("filter error") 927 }, 928 err: extendPropertyErrorf("s", "filter error"), 929 }, 930 } 931} 932 933func TestAppendProperties(t *testing.T) { 934 for _, testCase := range appendPropertiesTestCases() { 935 testString := fmt.Sprintf("%v, %v -> %v", testCase.in1, testCase.in2, testCase.out) 936 937 got := testCase.in1 938 var err error 939 var testType string 940 941 if testCase.prepend { 942 testType = "prepend" 943 err = PrependProperties(got, testCase.in2, testCase.filter) 944 } else { 945 testType = "append" 946 err = AppendProperties(got, testCase.in2, testCase.filter) 947 } 948 949 check(t, testType, testString, got, err, testCase.out, testCase.err) 950 } 951} 952 953func TestExtendProperties(t *testing.T) { 954 for _, testCase := range appendPropertiesTestCases() { 955 testString := fmt.Sprintf("%v, %v -> %v", testCase.in1, testCase.in2, testCase.out) 956 957 got := testCase.in1 958 var err error 959 var testType string 960 961 order := func(property string, 962 dstField, srcField reflect.StructField, 963 dstValue, srcValue interface{}) (Order, error) { 964 if testCase.prepend { 965 return Prepend, nil 966 } else { 967 return Append, nil 968 } 969 } 970 971 if testCase.prepend { 972 testType = "prepend" 973 } else { 974 testType = "append" 975 } 976 977 err = ExtendProperties(got, testCase.in2, testCase.filter, order) 978 979 check(t, testType, testString, got, err, testCase.out, testCase.err) 980 } 981} 982 983type appendMatchingPropertiesTestCase struct { 984 in1 []interface{} 985 in2 interface{} 986 out []interface{} 987 prepend bool 988 filter ExtendPropertyFilterFunc 989 err error 990} 991 992func appendMatchingPropertiesTestCases() []appendMatchingPropertiesTestCase { 993 return []appendMatchingPropertiesTestCase{ 994 { 995 // Append strings 996 in1: []interface{}{&struct{ S string }{ 997 S: "string1", 998 }}, 999 in2: &struct{ S string }{ 1000 S: "string2", 1001 }, 1002 out: []interface{}{&struct{ S string }{ 1003 S: "string1string2", 1004 }}, 1005 }, 1006 { 1007 // Prepend strings 1008 in1: []interface{}{&struct{ S string }{ 1009 S: "string1", 1010 }}, 1011 in2: &struct{ S string }{ 1012 S: "string2", 1013 }, 1014 out: []interface{}{&struct{ S string }{ 1015 S: "string2string1", 1016 }}, 1017 prepend: true, 1018 }, 1019 { 1020 // Append all 1021 in1: []interface{}{ 1022 &struct{ S, A string }{ 1023 S: "string1", 1024 }, 1025 &struct{ S, B string }{ 1026 S: "string2", 1027 }, 1028 }, 1029 in2: &struct{ S string }{ 1030 S: "string3", 1031 }, 1032 out: []interface{}{ 1033 &struct{ S, A string }{ 1034 S: "string1string3", 1035 }, 1036 &struct{ S, B string }{ 1037 S: "string2string3", 1038 }, 1039 }, 1040 }, 1041 { 1042 // Append some 1043 in1: []interface{}{ 1044 &struct{ S, A string }{ 1045 S: "string1", 1046 }, 1047 &struct{ B string }{}, 1048 }, 1049 in2: &struct{ S string }{ 1050 S: "string2", 1051 }, 1052 out: []interface{}{ 1053 &struct{ S, A string }{ 1054 S: "string1string2", 1055 }, 1056 &struct{ B string }{}, 1057 }, 1058 }, 1059 { 1060 // Append mismatched structs 1061 in1: []interface{}{&struct{ S, A string }{ 1062 S: "string1", 1063 }}, 1064 in2: &struct{ S string }{ 1065 S: "string2", 1066 }, 1067 out: []interface{}{&struct{ S, A string }{ 1068 S: "string1string2", 1069 }}, 1070 }, 1071 { 1072 // Append mismatched pointer structs 1073 in1: []interface{}{&struct{ S *struct{ S, A string } }{ 1074 S: &struct{ S, A string }{ 1075 S: "string1", 1076 }, 1077 }}, 1078 in2: &struct{ S *struct{ S string } }{ 1079 S: &struct{ S string }{ 1080 S: "string2", 1081 }, 1082 }, 1083 out: []interface{}{&struct{ S *struct{ S, A string } }{ 1084 S: &struct{ S, A string }{ 1085 S: "string1string2", 1086 }, 1087 }}, 1088 }, 1089 { 1090 // Append through mismatched types 1091 in1: []interface{}{ 1092 &struct{ B string }{}, 1093 &struct{ S interface{} }{ 1094 S: &struct{ S, A string }{ 1095 S: "string1", 1096 }, 1097 }, 1098 }, 1099 in2: &struct{ S struct{ S string } }{ 1100 S: struct{ S string }{ 1101 S: "string2", 1102 }, 1103 }, 1104 out: []interface{}{ 1105 &struct{ B string }{}, 1106 &struct{ S interface{} }{ 1107 S: &struct{ S, A string }{ 1108 S: "string1string2", 1109 }, 1110 }, 1111 }, 1112 }, 1113 { 1114 // Append through mismatched types and nil 1115 in1: []interface{}{ 1116 &struct{ B string }{}, 1117 &struct{ S interface{} }{ 1118 S: (*struct{ S, A string })(nil), 1119 }, 1120 }, 1121 in2: &struct{ S struct{ S string } }{ 1122 S: struct{ S string }{ 1123 S: "string2", 1124 }, 1125 }, 1126 out: []interface{}{ 1127 &struct{ B string }{}, 1128 &struct{ S interface{} }{ 1129 S: &struct{ S, A string }{ 1130 S: "string2", 1131 }, 1132 }, 1133 }, 1134 }, 1135 { 1136 // Append through multiple matches 1137 in1: []interface{}{ 1138 &struct { 1139 S struct{ S, A string } 1140 }{ 1141 S: struct{ S, A string }{ 1142 S: "string1", 1143 }, 1144 }, 1145 &struct { 1146 S struct{ S, B string } 1147 }{ 1148 S: struct{ S, B string }{ 1149 S: "string2", 1150 }, 1151 }, 1152 }, 1153 in2: &struct{ S struct{ B string } }{ 1154 S: struct{ B string }{ 1155 B: "string3", 1156 }, 1157 }, 1158 out: []interface{}{ 1159 &struct { 1160 S struct{ S, A string } 1161 }{ 1162 S: struct{ S, A string }{ 1163 S: "string1", 1164 }, 1165 }, 1166 &struct { 1167 S struct{ S, B string } 1168 }{ 1169 S: struct{ S, B string }{ 1170 S: "string2", 1171 B: "string3", 1172 }, 1173 }, 1174 }, 1175 }, 1176 1177 // Errors 1178 1179 { 1180 // Non-pointer in1 1181 in1: []interface{}{struct{}{}}, 1182 in2: &struct{}{}, 1183 err: errors.New("expected pointer to struct, got struct {}"), 1184 out: []interface{}{struct{}{}}, 1185 }, 1186 { 1187 // Non-pointer in2 1188 in1: []interface{}{&struct{}{}}, 1189 in2: struct{}{}, 1190 err: errors.New("expected pointer to struct, got struct {}"), 1191 out: []interface{}{&struct{}{}}, 1192 }, 1193 { 1194 // Non-struct in1 1195 in1: []interface{}{&[]string{"bad"}}, 1196 in2: &struct{}{}, 1197 err: errors.New("expected pointer to struct, got *[]string"), 1198 out: []interface{}{&[]string{"bad"}}, 1199 }, 1200 { 1201 // Non-struct in2 1202 in1: []interface{}{&struct{}{}}, 1203 in2: &[]string{"bad"}, 1204 err: errors.New("expected pointer to struct, got *[]string"), 1205 out: []interface{}{&struct{}{}}, 1206 }, 1207 { 1208 // Append none 1209 in1: []interface{}{ 1210 &struct{ A string }{}, 1211 &struct{ B string }{}, 1212 }, 1213 in2: &struct{ S string }{ 1214 S: "string1", 1215 }, 1216 out: []interface{}{ 1217 &struct{ A string }{}, 1218 &struct{ B string }{}, 1219 }, 1220 err: extendPropertyErrorf("s", "failed to find property to extend"), 1221 }, 1222 { 1223 // Append mismatched kinds 1224 in1: []interface{}{ 1225 &struct{ S string }{ 1226 S: "string1", 1227 }, 1228 }, 1229 in2: &struct{ S []string }{ 1230 S: []string{"string2"}, 1231 }, 1232 out: []interface{}{ 1233 &struct{ S string }{ 1234 S: "string1", 1235 }, 1236 }, 1237 err: extendPropertyErrorf("s", "mismatched types string and []string"), 1238 }, 1239 { 1240 // Append mismatched types 1241 in1: []interface{}{ 1242 &struct{ S []int }{ 1243 S: []int{1}, 1244 }, 1245 }, 1246 in2: &struct{ S []string }{ 1247 S: []string{"string2"}, 1248 }, 1249 out: []interface{}{ 1250 &struct{ S []int }{ 1251 S: []int{1}, 1252 }, 1253 }, 1254 err: extendPropertyErrorf("s", "mismatched types []int and []string"), 1255 }, 1256 } 1257} 1258 1259func TestAppendMatchingProperties(t *testing.T) { 1260 for _, testCase := range appendMatchingPropertiesTestCases() { 1261 testString := fmt.Sprintf("%s, %s -> %s", p(testCase.in1), p(testCase.in2), p(testCase.out)) 1262 1263 got := testCase.in1 1264 var err error 1265 var testType string 1266 1267 if testCase.prepend { 1268 testType = "prepend matching" 1269 err = PrependMatchingProperties(got, testCase.in2, testCase.filter) 1270 } else { 1271 testType = "append matching" 1272 err = AppendMatchingProperties(got, testCase.in2, testCase.filter) 1273 } 1274 1275 check(t, testType, testString, got, err, testCase.out, testCase.err) 1276 } 1277} 1278 1279func TestExtendMatchingProperties(t *testing.T) { 1280 for _, testCase := range appendMatchingPropertiesTestCases() { 1281 testString := fmt.Sprintf("%s, %s -> %s", p(testCase.in1), p(testCase.in2), p(testCase.out)) 1282 1283 got := testCase.in1 1284 var err error 1285 var testType string 1286 1287 order := func(property string, 1288 dstField, srcField reflect.StructField, 1289 dstValue, srcValue interface{}) (Order, error) { 1290 if testCase.prepend { 1291 return Prepend, nil 1292 } else { 1293 return Append, nil 1294 } 1295 } 1296 1297 if testCase.prepend { 1298 testType = "prepend matching" 1299 } else { 1300 testType = "append matching" 1301 } 1302 1303 err = ExtendMatchingProperties(got, testCase.in2, testCase.filter, order) 1304 1305 check(t, testType, testString, got, err, testCase.out, testCase.err) 1306 } 1307} 1308 1309func check(t *testing.T, testType, testString string, 1310 got interface{}, err error, 1311 expected interface{}, expectedErr error) { 1312 1313 printedTestCase := false 1314 e := func(s string, expected, got interface{}) { 1315 if !printedTestCase { 1316 t.Errorf("test case %s: %s", testType, testString) 1317 printedTestCase = true 1318 } 1319 t.Errorf("incorrect %s", s) 1320 t.Errorf(" expected: %s", p(expected)) 1321 t.Errorf(" got: %s", p(got)) 1322 } 1323 1324 if err != nil { 1325 if expectedErr != nil { 1326 if err.Error() != expectedErr.Error() { 1327 e("unexpected error", expectedErr.Error(), err.Error()) 1328 } 1329 } else { 1330 e("unexpected error", nil, err.Error()) 1331 } 1332 } else { 1333 if expectedErr != nil { 1334 e("missing error", expectedErr, nil) 1335 } 1336 } 1337 1338 if !reflect.DeepEqual(expected, got) { 1339 e("output:", expected, got) 1340 } 1341} 1342 1343func p(in interface{}) string { 1344 if v, ok := in.([]interface{}); ok { 1345 s := make([]string, len(v)) 1346 for i := range v { 1347 s[i] = fmt.Sprintf("%#v", v[i]) 1348 } 1349 return "[" + strings.Join(s, ", ") + "]" 1350 } else { 1351 return fmt.Sprintf("%#v", in) 1352 } 1353} 1354