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 order Order // default is Append 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 order: Prepend, 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 order: Prepend, 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 order: Prepend, 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 order: Prepend, 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 order: Prepend, 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 order: Prepend, 290 }, 291 { 292 // Replace slice 293 in1: &struct{ S []string }{ 294 S: []string{"string1"}, 295 }, 296 in2: &struct{ S []string }{ 297 S: []string{"string2"}, 298 }, 299 out: &struct{ S []string }{ 300 S: []string{"string2"}, 301 }, 302 order: Replace, 303 }, 304 { 305 // Append empty slice 306 in1: &struct{ S1, S2 []string }{ 307 S1: []string{"string1"}, 308 S2: []string{}, 309 }, 310 in2: &struct{ S1, S2 []string }{ 311 S1: []string{}, 312 S2: []string{"string2"}, 313 }, 314 out: &struct{ S1, S2 []string }{ 315 S1: []string{"string1"}, 316 S2: []string{"string2"}, 317 }, 318 }, 319 { 320 // Prepend empty slice 321 in1: &struct{ S1, S2 []string }{ 322 S1: []string{"string1"}, 323 S2: []string{}, 324 }, 325 in2: &struct{ S1, S2 []string }{ 326 S1: []string{}, 327 S2: []string{"string2"}, 328 }, 329 out: &struct{ S1, S2 []string }{ 330 S1: []string{"string1"}, 331 S2: []string{"string2"}, 332 }, 333 order: Prepend, 334 }, 335 { 336 // Replace empty slice 337 in1: &struct{ S1, S2 []string }{ 338 S1: []string{"string1"}, 339 S2: []string{}, 340 }, 341 in2: &struct{ S1, S2 []string }{ 342 S1: []string{}, 343 S2: []string{"string2"}, 344 }, 345 out: &struct{ S1, S2 []string }{ 346 S1: []string{}, 347 S2: []string{"string2"}, 348 }, 349 order: Replace, 350 }, 351 { 352 // Append nil slice 353 in1: &struct{ S1, S2, S3 []string }{ 354 S1: []string{"string1"}, 355 }, 356 in2: &struct{ S1, S2, S3 []string }{ 357 S2: []string{"string2"}, 358 }, 359 out: &struct{ S1, S2, S3 []string }{ 360 S1: []string{"string1"}, 361 S2: []string{"string2"}, 362 S3: nil, 363 }, 364 }, 365 { 366 // Prepend nil slice 367 in1: &struct{ S1, S2, S3 []string }{ 368 S1: []string{"string1"}, 369 }, 370 in2: &struct{ S1, S2, S3 []string }{ 371 S2: []string{"string2"}, 372 }, 373 out: &struct{ S1, S2, S3 []string }{ 374 S1: []string{"string1"}, 375 S2: []string{"string2"}, 376 S3: nil, 377 }, 378 order: Prepend, 379 }, 380 { 381 // Replace nil slice 382 in1: &struct{ S1, S2, S3 []string }{ 383 S1: []string{"string1"}, 384 }, 385 in2: &struct{ S1, S2, S3 []string }{ 386 S2: []string{"string2"}, 387 }, 388 out: &struct{ S1, S2, S3 []string }{ 389 S1: []string{"string1"}, 390 S2: []string{"string2"}, 391 S3: nil, 392 }, 393 order: Replace, 394 }, 395 { 396 // Replace embedded slice 397 in1: &struct{ S *struct{ S1 []string } }{ 398 S: &struct{ S1 []string }{ 399 S1: []string{"string1"}, 400 }, 401 }, 402 in2: &struct{ S *struct{ S1 []string } }{ 403 S: &struct{ S1 []string }{ 404 S1: []string{"string2"}, 405 }, 406 }, 407 out: &struct{ S *struct{ S1 []string } }{ 408 S: &struct{ S1 []string }{ 409 S1: []string{"string2"}, 410 }, 411 }, 412 order: Replace, 413 }, 414 { 415 // Append slice of structs 416 in1: &struct{ S []struct{ F string } }{ 417 S: []struct{ F string }{ 418 {F: "foo"}, {F: "bar"}, 419 }, 420 }, 421 in2: &struct{ S []struct{ F string } }{ 422 S: []struct{ F string }{ 423 {F: "baz"}, 424 }, 425 }, 426 out: &struct{ S []struct{ F string } }{ 427 S: []struct{ F string }{ 428 {F: "foo"}, {F: "bar"}, {F: "baz"}, 429 }, 430 }, 431 order: Append, 432 }, 433 { 434 // Prepend slice of structs 435 in1: &struct{ S []struct{ F string } }{ 436 S: []struct{ F string }{ 437 {F: "foo"}, {F: "bar"}, 438 }, 439 }, 440 in2: &struct{ S []struct{ F string } }{ 441 S: []struct{ F string }{ 442 {F: "baz"}, 443 }, 444 }, 445 out: &struct{ S []struct{ F string } }{ 446 S: []struct{ F string }{ 447 {F: "baz"}, {F: "foo"}, {F: "bar"}, 448 }, 449 }, 450 order: Prepend, 451 }, 452 { 453 // Replace slice of structs 454 in1: &struct{ S []struct{ F string } }{ 455 S: []struct{ F string }{ 456 {F: "foo"}, {F: "bar"}, 457 }, 458 }, 459 in2: &struct{ S []struct{ F string } }{ 460 S: []struct{ F string }{ 461 {F: "baz"}, 462 }, 463 }, 464 out: &struct{ S []struct{ F string } }{ 465 S: []struct{ F string }{ 466 {F: "baz"}, 467 }, 468 }, 469 order: Replace, 470 }, 471 { 472 // Append pointer 473 in1: &struct{ S *struct{ S string } }{ 474 S: &struct{ S string }{ 475 S: "string1", 476 }, 477 }, 478 in2: &struct{ S *struct{ S string } }{ 479 S: &struct{ S string }{ 480 S: "string2", 481 }, 482 }, 483 out: &struct{ S *struct{ S string } }{ 484 S: &struct{ S string }{ 485 S: "string1string2", 486 }, 487 }, 488 }, 489 { 490 // Prepend pointer 491 in1: &struct{ S *struct{ S string } }{ 492 S: &struct{ S string }{ 493 S: "string1", 494 }, 495 }, 496 in2: &struct{ S *struct{ S string } }{ 497 S: &struct{ S string }{ 498 S: "string2", 499 }, 500 }, 501 out: &struct{ S *struct{ S string } }{ 502 S: &struct{ S string }{ 503 S: "string2string1", 504 }, 505 }, 506 order: Prepend, 507 }, 508 { 509 // Append interface 510 in1: &struct{ S interface{} }{ 511 S: &struct{ S string }{ 512 S: "string1", 513 }, 514 }, 515 in2: &struct{ S interface{} }{ 516 S: &struct{ S string }{ 517 S: "string2", 518 }, 519 }, 520 out: &struct{ S interface{} }{ 521 S: &struct{ S string }{ 522 S: "string1string2", 523 }, 524 }, 525 }, 526 { 527 // Prepend interface 528 in1: &struct{ S interface{} }{ 529 S: &struct{ S string }{ 530 S: "string1", 531 }, 532 }, 533 in2: &struct{ S interface{} }{ 534 S: &struct{ S string }{ 535 S: "string2", 536 }, 537 }, 538 out: &struct{ S interface{} }{ 539 S: &struct{ S string }{ 540 S: "string2string1", 541 }, 542 }, 543 order: Prepend, 544 }, 545 { 546 // Unexported field 547 in1: &struct{ s string }{ 548 s: "string1", 549 }, 550 in2: &struct{ s string }{ 551 s: "string2", 552 }, 553 out: &struct{ s string }{ 554 s: "string1", 555 }, 556 }, 557 { 558 // Unexported field 559 in1: &struct{ i *int64 }{ 560 i: Int64Ptr(33), 561 }, 562 in2: &struct{ i *int64 }{ 563 i: Int64Ptr(5), 564 }, 565 out: &struct{ i *int64 }{ 566 i: Int64Ptr(33), 567 }, 568 }, 569 { 570 // Empty struct 571 in1: &struct{}{}, 572 in2: &struct{}{}, 573 out: &struct{}{}, 574 }, 575 { 576 // Interface nil 577 in1: &struct{ S interface{} }{ 578 S: nil, 579 }, 580 in2: &struct{ S interface{} }{ 581 S: nil, 582 }, 583 out: &struct{ S interface{} }{ 584 S: nil, 585 }, 586 }, 587 { 588 // Pointer nil 589 in1: &struct{ S *struct{} }{ 590 S: nil, 591 }, 592 in2: &struct{ S *struct{} }{ 593 S: nil, 594 }, 595 out: &struct{ S *struct{} }{ 596 S: nil, 597 }, 598 }, 599 { 600 // Anonymous struct 601 in1: &struct { 602 EmbeddedStruct 603 Nested struct{ EmbeddedStruct } 604 }{ 605 EmbeddedStruct: EmbeddedStruct{ 606 S: "string1", 607 I: Int64Ptr(55), 608 }, 609 Nested: struct{ EmbeddedStruct }{ 610 EmbeddedStruct: EmbeddedStruct{ 611 S: "string2", 612 I: Int64Ptr(-4), 613 }, 614 }, 615 }, 616 in2: &struct { 617 EmbeddedStruct 618 Nested struct{ EmbeddedStruct } 619 }{ 620 EmbeddedStruct: EmbeddedStruct{ 621 S: "string3", 622 I: Int64Ptr(66), 623 }, 624 Nested: struct{ EmbeddedStruct }{ 625 EmbeddedStruct: EmbeddedStruct{ 626 S: "string4", 627 I: Int64Ptr(-8), 628 }, 629 }, 630 }, 631 out: &struct { 632 EmbeddedStruct 633 Nested struct{ EmbeddedStruct } 634 }{ 635 EmbeddedStruct: EmbeddedStruct{ 636 S: "string1string3", 637 I: Int64Ptr(66), 638 }, 639 Nested: struct{ EmbeddedStruct }{ 640 EmbeddedStruct: EmbeddedStruct{ 641 S: "string2string4", 642 I: Int64Ptr(-8), 643 }, 644 }, 645 }, 646 }, 647 { 648 // Anonymous interface 649 in1: &struct { 650 EmbeddedInterface 651 Nested struct{ EmbeddedInterface } 652 }{ 653 EmbeddedInterface: &struct { 654 S string 655 I *int64 656 }{ 657 S: "string1", 658 I: Int64Ptr(-8), 659 }, 660 Nested: struct{ EmbeddedInterface }{ 661 EmbeddedInterface: &struct { 662 S string 663 I *int64 664 }{ 665 S: "string2", 666 I: Int64Ptr(55), 667 }, 668 }, 669 }, 670 in2: &struct { 671 EmbeddedInterface 672 Nested struct{ EmbeddedInterface } 673 }{ 674 EmbeddedInterface: &struct { 675 S string 676 I *int64 677 }{ 678 S: "string3", 679 I: Int64Ptr(6), 680 }, 681 Nested: struct{ EmbeddedInterface }{ 682 EmbeddedInterface: &struct { 683 S string 684 I *int64 685 }{ 686 S: "string4", 687 I: Int64Ptr(6), 688 }, 689 }, 690 }, 691 out: &struct { 692 EmbeddedInterface 693 Nested struct{ EmbeddedInterface } 694 }{ 695 EmbeddedInterface: &struct { 696 S string 697 I *int64 698 }{ 699 S: "string1string3", 700 I: Int64Ptr(6), 701 }, 702 Nested: struct{ EmbeddedInterface }{ 703 EmbeddedInterface: &struct { 704 S string 705 I *int64 706 }{ 707 S: "string2string4", 708 I: Int64Ptr(6), 709 }, 710 }, 711 }, 712 }, 713 { 714 // Nil pointer to a struct 715 in1: &struct { 716 Nested *struct { 717 S string 718 } 719 }{}, 720 in2: &struct { 721 Nested *struct { 722 S string 723 } 724 }{ 725 Nested: &struct { 726 S string 727 }{ 728 S: "string", 729 }, 730 }, 731 out: &struct { 732 Nested *struct { 733 S string 734 } 735 }{ 736 Nested: &struct { 737 S string 738 }{ 739 S: "string", 740 }, 741 }, 742 }, 743 { 744 // Nil pointer to a struct in an interface 745 in1: &struct { 746 Nested interface{} 747 }{ 748 Nested: (*struct{ S string })(nil), 749 }, 750 in2: &struct { 751 Nested interface{} 752 }{ 753 Nested: &struct { 754 S string 755 }{ 756 S: "string", 757 }, 758 }, 759 out: &struct { 760 Nested interface{} 761 }{ 762 Nested: &struct { 763 S string 764 }{ 765 S: "string", 766 }, 767 }, 768 }, 769 { 770 // Interface src nil 771 in1: &struct{ S interface{} }{ 772 S: &struct{ S string }{ 773 S: "string1", 774 }, 775 }, 776 in2: &struct{ S interface{} }{ 777 S: nil, 778 }, 779 out: &struct{ S interface{} }{ 780 S: &struct{ S string }{ 781 S: "string1", 782 }, 783 }, 784 }, 785 786 // Errors 787 788 { 789 // Non-pointer in1 790 in1: struct{}{}, 791 in2: &struct{}{}, 792 err: errors.New("expected pointer to struct, got struct {}"), 793 out: struct{}{}, 794 }, 795 { 796 // Non-pointer in2 797 in1: &struct{}{}, 798 in2: struct{}{}, 799 err: errors.New("expected pointer to struct, got struct {}"), 800 out: &struct{}{}, 801 }, 802 { 803 // Non-struct in1 804 in1: &[]string{"bad"}, 805 in2: &struct{}{}, 806 err: errors.New("expected pointer to struct, got *[]string"), 807 out: &[]string{"bad"}, 808 }, 809 { 810 // Non-struct in2 811 in1: &struct{}{}, 812 in2: &[]string{"bad"}, 813 err: errors.New("expected pointer to struct, got *[]string"), 814 out: &struct{}{}, 815 }, 816 { 817 // Mismatched types 818 in1: &struct{ A string }{ 819 A: "string1", 820 }, 821 in2: &struct{ B string }{ 822 B: "string2", 823 }, 824 out: &struct{ A string }{ 825 A: "string1", 826 }, 827 err: errors.New("expected matching types for dst and src, got *struct { A string } and *struct { B string }"), 828 }, 829 { 830 // Unsupported kind 831 in1: &struct{ I int }{ 832 I: 1, 833 }, 834 in2: &struct{ I int }{ 835 I: 2, 836 }, 837 out: &struct{ I int }{ 838 I: 1, 839 }, 840 err: extendPropertyErrorf("i", "unsupported kind int"), 841 }, 842 { 843 // Unsupported kind 844 in1: &struct{ I int64 }{ 845 I: 1, 846 }, 847 in2: &struct{ I int64 }{ 848 I: 2, 849 }, 850 out: &struct{ I int64 }{ 851 I: 1, 852 }, 853 err: extendPropertyErrorf("i", "unsupported kind int64"), 854 }, 855 { 856 // Interface nilitude mismatch 857 in1: &struct{ S interface{} }{ 858 S: nil, 859 }, 860 in2: &struct{ S interface{} }{ 861 S: &struct{ S string }{ 862 S: "string1", 863 }, 864 }, 865 out: &struct{ S interface{} }{ 866 S: nil, 867 }, 868 err: extendPropertyErrorf("s", "nilitude mismatch"), 869 }, 870 { 871 // Interface type mismatch 872 in1: &struct{ S interface{} }{ 873 S: &struct{ A string }{ 874 A: "string1", 875 }, 876 }, 877 in2: &struct{ S interface{} }{ 878 S: &struct{ B string }{ 879 B: "string2", 880 }, 881 }, 882 out: &struct{ S interface{} }{ 883 S: &struct{ A string }{ 884 A: "string1", 885 }, 886 }, 887 err: extendPropertyErrorf("s", "mismatched types struct { A string } and struct { B string }"), 888 }, 889 { 890 // Interface not a pointer 891 in1: &struct{ S interface{} }{ 892 S: struct{ S string }{ 893 S: "string1", 894 }, 895 }, 896 in2: &struct{ S interface{} }{ 897 S: struct{ S string }{ 898 S: "string2", 899 }, 900 }, 901 out: &struct{ S interface{} }{ 902 S: struct{ S string }{ 903 S: "string1", 904 }, 905 }, 906 err: extendPropertyErrorf("s", "interface not a pointer"), 907 }, 908 { 909 // Pointer not a struct 910 in1: &struct{ S *[]string }{ 911 S: &[]string{"string1"}, 912 }, 913 in2: &struct{ S *[]string }{ 914 S: &[]string{"string2"}, 915 }, 916 out: &struct{ S *[]string }{ 917 S: &[]string{"string1"}, 918 }, 919 err: extendPropertyErrorf("s", "pointer is a slice"), 920 }, 921 { 922 // Error in nested struct 923 in1: &struct{ S interface{} }{ 924 S: &struct{ I int }{ 925 I: 1, 926 }, 927 }, 928 in2: &struct{ S interface{} }{ 929 S: &struct{ I int }{ 930 I: 2, 931 }, 932 }, 933 out: &struct{ S interface{} }{ 934 S: &struct{ I int }{ 935 I: 1, 936 }, 937 }, 938 err: extendPropertyErrorf("s.i", "unsupported kind int"), 939 }, 940 941 // Filters 942 943 { 944 // Filter true 945 in1: &struct{ S string }{ 946 S: "string1", 947 }, 948 in2: &struct{ S string }{ 949 S: "string2", 950 }, 951 out: &struct{ S string }{ 952 S: "string1string2", 953 }, 954 filter: func(property string, 955 dstField, srcField reflect.StructField, 956 dstValue, srcValue interface{}) (bool, error) { 957 return true, nil 958 }, 959 }, 960 { 961 // Filter false 962 in1: &struct{ S string }{ 963 S: "string1", 964 }, 965 in2: &struct{ S string }{ 966 S: "string2", 967 }, 968 out: &struct{ S string }{ 969 S: "string1", 970 }, 971 filter: func(property string, 972 dstField, srcField reflect.StructField, 973 dstValue, srcValue interface{}) (bool, error) { 974 return false, nil 975 }, 976 }, 977 { 978 // Filter check args 979 in1: &struct{ S string }{ 980 S: "string1", 981 }, 982 in2: &struct{ S string }{ 983 S: "string2", 984 }, 985 out: &struct{ S string }{ 986 S: "string1string2", 987 }, 988 filter: func(property string, 989 dstField, srcField reflect.StructField, 990 dstValue, srcValue interface{}) (bool, error) { 991 return property == "s" && 992 dstField.Name == "S" && srcField.Name == "S" && 993 dstValue.(string) == "string1" && srcValue.(string) == "string2", nil 994 }, 995 }, 996 { 997 // Filter mutated 998 in1: &struct { 999 S string `blueprint:"mutated"` 1000 }{ 1001 S: "string1", 1002 }, 1003 in2: &struct { 1004 S string `blueprint:"mutated"` 1005 }{ 1006 S: "string2", 1007 }, 1008 out: &struct { 1009 S string `blueprint:"mutated"` 1010 }{ 1011 S: "string1", 1012 }, 1013 }, 1014 { 1015 // Filter mutated 1016 in1: &struct { 1017 S *int64 `blueprint:"mutated"` 1018 }{ 1019 S: Int64Ptr(4), 1020 }, 1021 in2: &struct { 1022 S *int64 `blueprint:"mutated"` 1023 }{ 1024 S: Int64Ptr(5), 1025 }, 1026 out: &struct { 1027 S *int64 `blueprint:"mutated"` 1028 }{ 1029 S: Int64Ptr(4), 1030 }, 1031 }, 1032 { 1033 // Filter error 1034 in1: &struct{ S string }{ 1035 S: "string1", 1036 }, 1037 in2: &struct{ S string }{ 1038 S: "string2", 1039 }, 1040 out: &struct{ S string }{ 1041 S: "string1", 1042 }, 1043 filter: func(property string, 1044 dstField, srcField reflect.StructField, 1045 dstValue, srcValue interface{}) (bool, error) { 1046 return true, fmt.Errorf("filter error") 1047 }, 1048 err: extendPropertyErrorf("s", "filter error"), 1049 }, 1050 } 1051} 1052 1053func TestAppendProperties(t *testing.T) { 1054 for _, testCase := range appendPropertiesTestCases() { 1055 testString := fmt.Sprintf("%v, %v -> %v", testCase.in1, testCase.in2, testCase.out) 1056 1057 got := testCase.in1 1058 var err error 1059 var testType string 1060 1061 switch testCase.order { 1062 case Append: 1063 testType = "append" 1064 err = AppendProperties(got, testCase.in2, testCase.filter) 1065 case Prepend: 1066 testType = "prepend" 1067 err = PrependProperties(got, testCase.in2, testCase.filter) 1068 case Replace: 1069 testType = "replace" 1070 err = ExtendProperties(got, testCase.in2, testCase.filter, OrderReplace) 1071 } 1072 1073 check(t, testType, testString, got, err, testCase.out, testCase.err) 1074 } 1075} 1076 1077func TestExtendProperties(t *testing.T) { 1078 for _, testCase := range appendPropertiesTestCases() { 1079 testString := fmt.Sprintf("%v, %v -> %v", testCase.in1, testCase.in2, testCase.out) 1080 1081 got := testCase.in1 1082 var err error 1083 var testType string 1084 1085 order := func(property string, 1086 dstField, srcField reflect.StructField, 1087 dstValue, srcValue interface{}) (Order, error) { 1088 switch testCase.order { 1089 case Append: 1090 return Append, nil 1091 case Prepend: 1092 return Prepend, nil 1093 case Replace: 1094 return Replace, nil 1095 } 1096 return Append, errors.New("unknown order") 1097 } 1098 1099 switch testCase.order { 1100 case Append: 1101 testType = "prepend" 1102 case Prepend: 1103 testType = "append" 1104 case Replace: 1105 testType = "replace" 1106 } 1107 1108 err = ExtendProperties(got, testCase.in2, testCase.filter, order) 1109 1110 check(t, testType, testString, got, err, testCase.out, testCase.err) 1111 } 1112} 1113 1114type appendMatchingPropertiesTestCase struct { 1115 in1 []interface{} 1116 in2 interface{} 1117 out []interface{} 1118 order Order // default is Append 1119 filter ExtendPropertyFilterFunc 1120 err error 1121} 1122 1123func appendMatchingPropertiesTestCases() []appendMatchingPropertiesTestCase { 1124 return []appendMatchingPropertiesTestCase{ 1125 { 1126 // Append strings 1127 in1: []interface{}{&struct{ S string }{ 1128 S: "string1", 1129 }}, 1130 in2: &struct{ S string }{ 1131 S: "string2", 1132 }, 1133 out: []interface{}{&struct{ S string }{ 1134 S: "string1string2", 1135 }}, 1136 }, 1137 { 1138 // Prepend strings 1139 in1: []interface{}{&struct{ S string }{ 1140 S: "string1", 1141 }}, 1142 in2: &struct{ S string }{ 1143 S: "string2", 1144 }, 1145 out: []interface{}{&struct{ S string }{ 1146 S: "string2string1", 1147 }}, 1148 order: Prepend, 1149 }, 1150 { 1151 // Append all 1152 in1: []interface{}{ 1153 &struct{ S, A string }{ 1154 S: "string1", 1155 }, 1156 &struct{ S, B string }{ 1157 S: "string2", 1158 }, 1159 }, 1160 in2: &struct{ S string }{ 1161 S: "string3", 1162 }, 1163 out: []interface{}{ 1164 &struct{ S, A string }{ 1165 S: "string1string3", 1166 }, 1167 &struct{ S, B string }{ 1168 S: "string2string3", 1169 }, 1170 }, 1171 }, 1172 { 1173 // Append some 1174 in1: []interface{}{ 1175 &struct{ S, A string }{ 1176 S: "string1", 1177 }, 1178 &struct{ B string }{}, 1179 }, 1180 in2: &struct{ S string }{ 1181 S: "string2", 1182 }, 1183 out: []interface{}{ 1184 &struct{ S, A string }{ 1185 S: "string1string2", 1186 }, 1187 &struct{ B string }{}, 1188 }, 1189 }, 1190 { 1191 // Append mismatched structs 1192 in1: []interface{}{&struct{ S, A string }{ 1193 S: "string1", 1194 }}, 1195 in2: &struct{ S string }{ 1196 S: "string2", 1197 }, 1198 out: []interface{}{&struct{ S, A string }{ 1199 S: "string1string2", 1200 }}, 1201 }, 1202 { 1203 // Append mismatched pointer structs 1204 in1: []interface{}{&struct{ S *struct{ S, A string } }{ 1205 S: &struct{ S, A string }{ 1206 S: "string1", 1207 }, 1208 }}, 1209 in2: &struct{ S *struct{ S string } }{ 1210 S: &struct{ S string }{ 1211 S: "string2", 1212 }, 1213 }, 1214 out: []interface{}{&struct{ S *struct{ S, A string } }{ 1215 S: &struct{ S, A string }{ 1216 S: "string1string2", 1217 }, 1218 }}, 1219 }, 1220 { 1221 // Append through mismatched types 1222 in1: []interface{}{ 1223 &struct{ B string }{}, 1224 &struct{ S interface{} }{ 1225 S: &struct{ S, A string }{ 1226 S: "string1", 1227 }, 1228 }, 1229 }, 1230 in2: &struct{ S struct{ S string } }{ 1231 S: struct{ S string }{ 1232 S: "string2", 1233 }, 1234 }, 1235 out: []interface{}{ 1236 &struct{ B string }{}, 1237 &struct{ S interface{} }{ 1238 S: &struct{ S, A string }{ 1239 S: "string1string2", 1240 }, 1241 }, 1242 }, 1243 }, 1244 { 1245 // Append through mismatched types and nil 1246 in1: []interface{}{ 1247 &struct{ B string }{}, 1248 &struct{ S interface{} }{ 1249 S: (*struct{ S, A string })(nil), 1250 }, 1251 }, 1252 in2: &struct{ S struct{ S string } }{ 1253 S: struct{ S string }{ 1254 S: "string2", 1255 }, 1256 }, 1257 out: []interface{}{ 1258 &struct{ B string }{}, 1259 &struct{ S interface{} }{ 1260 S: &struct{ S, A string }{ 1261 S: "string2", 1262 }, 1263 }, 1264 }, 1265 }, 1266 { 1267 // Append through multiple matches 1268 in1: []interface{}{ 1269 &struct { 1270 S struct{ S, A string } 1271 }{ 1272 S: struct{ S, A string }{ 1273 S: "string1", 1274 }, 1275 }, 1276 &struct { 1277 S struct{ S, B string } 1278 }{ 1279 S: struct{ S, B string }{ 1280 S: "string2", 1281 }, 1282 }, 1283 }, 1284 in2: &struct{ S struct{ B string } }{ 1285 S: struct{ B string }{ 1286 B: "string3", 1287 }, 1288 }, 1289 out: []interface{}{ 1290 &struct { 1291 S struct{ S, A string } 1292 }{ 1293 S: struct{ S, A string }{ 1294 S: "string1", 1295 }, 1296 }, 1297 &struct { 1298 S struct{ S, B string } 1299 }{ 1300 S: struct{ S, B string }{ 1301 S: "string2", 1302 B: "string3", 1303 }, 1304 }, 1305 }, 1306 }, 1307 1308 // Errors 1309 1310 { 1311 // Non-pointer in1 1312 in1: []interface{}{struct{}{}}, 1313 in2: &struct{}{}, 1314 err: errors.New("expected pointer to struct, got struct {}"), 1315 out: []interface{}{struct{}{}}, 1316 }, 1317 { 1318 // Non-pointer in2 1319 in1: []interface{}{&struct{}{}}, 1320 in2: struct{}{}, 1321 err: errors.New("expected pointer to struct, got struct {}"), 1322 out: []interface{}{&struct{}{}}, 1323 }, 1324 { 1325 // Non-struct in1 1326 in1: []interface{}{&[]string{"bad"}}, 1327 in2: &struct{}{}, 1328 err: errors.New("expected pointer to struct, got *[]string"), 1329 out: []interface{}{&[]string{"bad"}}, 1330 }, 1331 { 1332 // Non-struct in2 1333 in1: []interface{}{&struct{}{}}, 1334 in2: &[]string{"bad"}, 1335 err: errors.New("expected pointer to struct, got *[]string"), 1336 out: []interface{}{&struct{}{}}, 1337 }, 1338 { 1339 // Append none 1340 in1: []interface{}{ 1341 &struct{ A string }{}, 1342 &struct{ B string }{}, 1343 }, 1344 in2: &struct{ S string }{ 1345 S: "string1", 1346 }, 1347 out: []interface{}{ 1348 &struct{ A string }{}, 1349 &struct{ B string }{}, 1350 }, 1351 err: extendPropertyErrorf("s", "failed to find property to extend"), 1352 }, 1353 { 1354 // Append mismatched kinds 1355 in1: []interface{}{ 1356 &struct{ S string }{ 1357 S: "string1", 1358 }, 1359 }, 1360 in2: &struct{ S []string }{ 1361 S: []string{"string2"}, 1362 }, 1363 out: []interface{}{ 1364 &struct{ S string }{ 1365 S: "string1", 1366 }, 1367 }, 1368 err: extendPropertyErrorf("s", "mismatched types string and []string"), 1369 }, 1370 { 1371 // Append mismatched types 1372 in1: []interface{}{ 1373 &struct{ S []int }{ 1374 S: []int{1}, 1375 }, 1376 }, 1377 in2: &struct{ S []string }{ 1378 S: []string{"string2"}, 1379 }, 1380 out: []interface{}{ 1381 &struct{ S []int }{ 1382 S: []int{1}, 1383 }, 1384 }, 1385 err: extendPropertyErrorf("s", "mismatched types []int and []string"), 1386 }, 1387 } 1388} 1389 1390func TestAppendMatchingProperties(t *testing.T) { 1391 for _, testCase := range appendMatchingPropertiesTestCases() { 1392 testString := fmt.Sprintf("%s, %s -> %s", p(testCase.in1), p(testCase.in2), p(testCase.out)) 1393 1394 got := testCase.in1 1395 var err error 1396 var testType string 1397 1398 switch testCase.order { 1399 case Append: 1400 testType = "append" 1401 err = AppendMatchingProperties(got, testCase.in2, testCase.filter) 1402 case Prepend: 1403 testType = "prepend" 1404 err = PrependMatchingProperties(got, testCase.in2, testCase.filter) 1405 case Replace: 1406 testType = "replace" 1407 err = ExtendMatchingProperties(got, testCase.in2, testCase.filter, OrderReplace) 1408 } 1409 1410 check(t, testType, testString, got, err, testCase.out, testCase.err) 1411 } 1412} 1413 1414func TestExtendMatchingProperties(t *testing.T) { 1415 for _, testCase := range appendMatchingPropertiesTestCases() { 1416 testString := fmt.Sprintf("%s, %s -> %s", p(testCase.in1), p(testCase.in2), p(testCase.out)) 1417 1418 got := testCase.in1 1419 var err error 1420 var testType string 1421 1422 order := func(property string, 1423 dstField, srcField reflect.StructField, 1424 dstValue, srcValue interface{}) (Order, error) { 1425 switch testCase.order { 1426 case Append: 1427 return Append, nil 1428 case Prepend: 1429 return Prepend, nil 1430 case Replace: 1431 return Replace, nil 1432 } 1433 return Append, errors.New("unknown order") 1434 } 1435 1436 switch testCase.order { 1437 case Append: 1438 testType = "prepend matching" 1439 case Prepend: 1440 testType = "append matching" 1441 case Replace: 1442 testType = "replace matching" 1443 } 1444 1445 err = ExtendMatchingProperties(got, testCase.in2, testCase.filter, order) 1446 1447 check(t, testType, testString, got, err, testCase.out, testCase.err) 1448 } 1449} 1450 1451func check(t *testing.T, testType, testString string, 1452 got interface{}, err error, 1453 expected interface{}, expectedErr error) { 1454 1455 printedTestCase := false 1456 e := func(s string, expected, got interface{}) { 1457 if !printedTestCase { 1458 t.Errorf("test case %s: %s", testType, testString) 1459 printedTestCase = true 1460 } 1461 t.Errorf("incorrect %s", s) 1462 t.Errorf(" expected: %s", p(expected)) 1463 t.Errorf(" got: %s", p(got)) 1464 } 1465 1466 if err != nil { 1467 if expectedErr != nil { 1468 if err.Error() != expectedErr.Error() { 1469 e("unexpected error", expectedErr.Error(), err.Error()) 1470 } 1471 } else { 1472 e("unexpected error", nil, err.Error()) 1473 } 1474 } else { 1475 if expectedErr != nil { 1476 e("missing error", expectedErr, nil) 1477 } 1478 } 1479 1480 if !reflect.DeepEqual(expected, got) { 1481 e("output:", expected, got) 1482 } 1483} 1484 1485func p(in interface{}) string { 1486 if v, ok := in.([]interface{}); ok { 1487 s := make([]string, len(v)) 1488 for i := range v { 1489 s[i] = fmt.Sprintf("%#v", v[i]) 1490 } 1491 return "[" + strings.Join(s, ", ") + "]" 1492 } else { 1493 return fmt.Sprintf("%#v", in) 1494 } 1495} 1496