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 name string 27 dst interface{} 28 src interface{} 29 out interface{} 30 order Order // default is Append 31 filter ExtendPropertyFilterFunc 32 err error 33} 34 35func appendPropertiesTestCases() []appendPropertyTestCase { 36 return []appendPropertyTestCase{ 37 // Valid inputs 38 39 { 40 name: "Append bool", 41 dst: &struct{ B1, B2, B3, B4 bool }{ 42 B1: true, 43 B2: false, 44 B3: true, 45 B4: false, 46 }, 47 src: &struct{ B1, B2, B3, B4 bool }{ 48 B1: true, 49 B2: true, 50 B3: false, 51 B4: false, 52 }, 53 out: &struct{ B1, B2, B3, B4 bool }{ 54 B1: true, 55 B2: true, 56 B3: true, 57 B4: false, 58 }, 59 }, 60 { 61 name: "Prepend bool", 62 dst: &struct{ B1, B2, B3, B4 bool }{ 63 B1: true, 64 B2: false, 65 B3: true, 66 B4: false, 67 }, 68 src: &struct{ B1, B2, B3, B4 bool }{ 69 B1: true, 70 B2: true, 71 B3: false, 72 B4: false, 73 }, 74 out: &struct{ B1, B2, B3, B4 bool }{ 75 B1: true, 76 B2: true, 77 B3: true, 78 B4: false, 79 }, 80 order: Prepend, 81 }, 82 { 83 name: "Append strings", 84 dst: &struct{ S string }{ 85 S: "string1", 86 }, 87 src: &struct{ S string }{ 88 S: "string2", 89 }, 90 out: &struct{ S string }{ 91 S: "string1string2", 92 }, 93 }, 94 { 95 name: "Prepend strings", 96 dst: &struct{ S string }{ 97 S: "string1", 98 }, 99 src: &struct{ S string }{ 100 S: "string2", 101 }, 102 out: &struct{ S string }{ 103 S: "string2string1", 104 }, 105 order: Prepend, 106 }, 107 { 108 name: "Append pointer to bool", 109 dst: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ 110 B1: BoolPtr(true), 111 B2: BoolPtr(false), 112 B3: nil, 113 B4: BoolPtr(true), 114 B5: BoolPtr(false), 115 B6: nil, 116 B7: BoolPtr(true), 117 B8: BoolPtr(false), 118 B9: nil, 119 }, 120 src: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ 121 B1: nil, 122 B2: nil, 123 B3: nil, 124 B4: BoolPtr(true), 125 B5: BoolPtr(true), 126 B6: BoolPtr(true), 127 B7: BoolPtr(false), 128 B8: BoolPtr(false), 129 B9: BoolPtr(false), 130 }, 131 out: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ 132 B1: BoolPtr(true), 133 B2: BoolPtr(false), 134 B3: nil, 135 B4: BoolPtr(true), 136 B5: BoolPtr(true), 137 B6: BoolPtr(true), 138 B7: BoolPtr(false), 139 B8: BoolPtr(false), 140 B9: BoolPtr(false), 141 }, 142 }, 143 { 144 name: "Prepend pointer to bool", 145 dst: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ 146 B1: BoolPtr(true), 147 B2: BoolPtr(false), 148 B3: nil, 149 B4: BoolPtr(true), 150 B5: BoolPtr(false), 151 B6: nil, 152 B7: BoolPtr(true), 153 B8: BoolPtr(false), 154 B9: nil, 155 }, 156 src: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ 157 B1: nil, 158 B2: nil, 159 B3: nil, 160 B4: BoolPtr(true), 161 B5: BoolPtr(true), 162 B6: BoolPtr(true), 163 B7: BoolPtr(false), 164 B8: BoolPtr(false), 165 B9: BoolPtr(false), 166 }, 167 out: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{ 168 B1: BoolPtr(true), 169 B2: BoolPtr(false), 170 B3: nil, 171 B4: BoolPtr(true), 172 B5: BoolPtr(false), 173 B6: BoolPtr(true), 174 B7: BoolPtr(true), 175 B8: BoolPtr(false), 176 B9: BoolPtr(false), 177 }, 178 order: Prepend, 179 }, 180 { 181 name: "Append pointer to integer", 182 dst: &struct{ I1, I2, I3, I4, I5, I6, I7, I8, I9 *int64 }{ 183 I1: Int64Ptr(55), 184 I2: Int64Ptr(-3), 185 I3: nil, 186 I4: Int64Ptr(100), 187 I5: Int64Ptr(33), 188 I6: nil, 189 I7: Int64Ptr(77), 190 I8: Int64Ptr(0), 191 I9: nil, 192 }, 193 src: &struct{ I1, I2, I3, I4, I5, I6, I7, I8, I9 *int64 }{ 194 I1: nil, 195 I2: nil, 196 I3: nil, 197 I4: Int64Ptr(1), 198 I5: Int64Ptr(-2), 199 I6: Int64Ptr(8), 200 I7: Int64Ptr(9), 201 I8: Int64Ptr(10), 202 I9: Int64Ptr(11), 203 }, 204 out: &struct{ I1, I2, I3, I4, I5, I6, I7, I8, I9 *int64 }{ 205 I1: Int64Ptr(55), 206 I2: Int64Ptr(-3), 207 I3: nil, 208 I4: Int64Ptr(1), 209 I5: Int64Ptr(-2), 210 I6: Int64Ptr(8), 211 I7: Int64Ptr(9), 212 I8: Int64Ptr(10), 213 I9: Int64Ptr(11), 214 }, 215 }, 216 { 217 name: "Prepend pointer to integer", 218 dst: &struct{ I1, I2, I3 *int64 }{ 219 I1: Int64Ptr(55), 220 I3: nil, 221 }, 222 src: &struct{ I1, I2, I3 *int64 }{ 223 I2: Int64Ptr(33), 224 }, 225 out: &struct{ I1, I2, I3 *int64 }{ 226 I1: Int64Ptr(55), 227 I2: Int64Ptr(33), 228 I3: nil, 229 }, 230 order: Prepend, 231 }, 232 { 233 name: "Append pointer to strings", 234 dst: &struct{ S1, S2, S3, S4 *string }{ 235 S1: StringPtr("string1"), 236 S2: StringPtr("string2"), 237 }, 238 src: &struct{ S1, S2, S3, S4 *string }{ 239 S1: StringPtr("string3"), 240 S3: StringPtr("string4"), 241 }, 242 out: &struct{ S1, S2, S3, S4 *string }{ 243 S1: StringPtr("string3"), 244 S2: StringPtr("string2"), 245 S3: StringPtr("string4"), 246 S4: nil, 247 }, 248 }, 249 { 250 name: "Prepend pointer to strings", 251 dst: &struct{ S1, S2, S3, S4 *string }{ 252 S1: StringPtr("string1"), 253 S2: StringPtr("string2"), 254 }, 255 src: &struct{ S1, S2, S3, S4 *string }{ 256 S1: StringPtr("string3"), 257 S3: StringPtr("string4"), 258 }, 259 out: &struct{ S1, S2, S3, S4 *string }{ 260 S1: StringPtr("string1"), 261 S2: StringPtr("string2"), 262 S3: StringPtr("string4"), 263 S4: nil, 264 }, 265 order: Prepend, 266 }, 267 { 268 name: "Append slice", 269 dst: &struct{ S []string }{ 270 S: []string{"string1"}, 271 }, 272 src: &struct{ S []string }{ 273 S: []string{"string2"}, 274 }, 275 out: &struct{ S []string }{ 276 S: []string{"string1", "string2"}, 277 }, 278 }, 279 { 280 name: "Prepend slice", 281 dst: &struct{ S []string }{ 282 S: []string{"string1"}, 283 }, 284 src: &struct{ S []string }{ 285 S: []string{"string2"}, 286 }, 287 out: &struct{ S []string }{ 288 S: []string{"string2", "string1"}, 289 }, 290 order: Prepend, 291 }, 292 { 293 name: "Replace slice", 294 dst: &struct{ S []string }{ 295 S: []string{"string1"}, 296 }, 297 src: &struct{ S []string }{ 298 S: []string{"string2"}, 299 }, 300 out: &struct{ S []string }{ 301 S: []string{"string2"}, 302 }, 303 order: Replace, 304 }, 305 { 306 name: "Append empty slice", 307 dst: &struct{ S1, S2 []string }{ 308 S1: []string{"string1"}, 309 S2: []string{}, 310 }, 311 src: &struct{ S1, S2 []string }{ 312 S1: []string{}, 313 S2: []string{"string2"}, 314 }, 315 out: &struct{ S1, S2 []string }{ 316 S1: []string{"string1"}, 317 S2: []string{"string2"}, 318 }, 319 }, 320 { 321 name: "Prepend empty slice", 322 dst: &struct{ S1, S2 []string }{ 323 S1: []string{"string1"}, 324 S2: []string{}, 325 }, 326 src: &struct{ S1, S2 []string }{ 327 S1: []string{}, 328 S2: []string{"string2"}, 329 }, 330 out: &struct{ S1, S2 []string }{ 331 S1: []string{"string1"}, 332 S2: []string{"string2"}, 333 }, 334 order: Prepend, 335 }, 336 { 337 name: "Replace empty slice", 338 dst: &struct{ S1, S2 []string }{ 339 S1: []string{"string1"}, 340 S2: []string{}, 341 }, 342 src: &struct{ S1, S2 []string }{ 343 S1: []string{}, 344 S2: []string{"string2"}, 345 }, 346 out: &struct{ S1, S2 []string }{ 347 S1: []string{}, 348 S2: []string{"string2"}, 349 }, 350 order: Replace, 351 }, 352 { 353 name: "Append nil slice", 354 dst: &struct{ S1, S2, S3 []string }{ 355 S1: []string{"string1"}, 356 }, 357 src: &struct{ S1, S2, S3 []string }{ 358 S2: []string{"string2"}, 359 }, 360 out: &struct{ S1, S2, S3 []string }{ 361 S1: []string{"string1"}, 362 S2: []string{"string2"}, 363 S3: nil, 364 }, 365 }, 366 { 367 name: "Prepend nil slice", 368 dst: &struct{ S1, S2, S3 []string }{ 369 S1: []string{"string1"}, 370 }, 371 src: &struct{ S1, S2, S3 []string }{ 372 S2: []string{"string2"}, 373 }, 374 out: &struct{ S1, S2, S3 []string }{ 375 S1: []string{"string1"}, 376 S2: []string{"string2"}, 377 S3: nil, 378 }, 379 order: Prepend, 380 }, 381 { 382 name: "Replace nil slice", 383 dst: &struct{ S1, S2, S3 []string }{ 384 S1: []string{"string1"}, 385 }, 386 src: &struct{ S1, S2, S3 []string }{ 387 S2: []string{"string2"}, 388 }, 389 out: &struct{ S1, S2, S3 []string }{ 390 S1: []string{"string1"}, 391 S2: []string{"string2"}, 392 S3: nil, 393 }, 394 order: Replace, 395 }, 396 { 397 name: "Replace embedded slice", 398 dst: &struct{ S *struct{ S1 []string } }{ 399 S: &struct{ S1 []string }{ 400 S1: []string{"string1"}, 401 }, 402 }, 403 src: &struct{ S *struct{ S1 []string } }{ 404 S: &struct{ S1 []string }{ 405 S1: []string{"string2"}, 406 }, 407 }, 408 out: &struct{ S *struct{ S1 []string } }{ 409 S: &struct{ S1 []string }{ 410 S1: []string{"string2"}, 411 }, 412 }, 413 order: Replace, 414 }, 415 { 416 name: "Append slice of structs", 417 dst: &struct{ S []struct{ F string } }{ 418 S: []struct{ F string }{ 419 {F: "foo"}, {F: "bar"}, 420 }, 421 }, 422 src: &struct{ S []struct{ F string } }{ 423 S: []struct{ F string }{ 424 {F: "baz"}, 425 }, 426 }, 427 out: &struct{ S []struct{ F string } }{ 428 S: []struct{ F string }{ 429 {F: "foo"}, {F: "bar"}, {F: "baz"}, 430 }, 431 }, 432 order: Append, 433 }, 434 { 435 name: "Prepend slice of structs", 436 dst: &struct{ S []struct{ F string } }{ 437 S: []struct{ F string }{ 438 {F: "foo"}, {F: "bar"}, 439 }, 440 }, 441 src: &struct{ S []struct{ F string } }{ 442 S: []struct{ F string }{ 443 {F: "baz"}, 444 }, 445 }, 446 out: &struct{ S []struct{ F string } }{ 447 S: []struct{ F string }{ 448 {F: "baz"}, {F: "foo"}, {F: "bar"}, 449 }, 450 }, 451 order: Prepend, 452 }, 453 { 454 name: "Append map", 455 dst: &struct{ S map[string]string }{ 456 S: map[string]string{ 457 "key0": "", 458 "key1": "dst_value1", 459 "key2": "dst_value2", 460 }, 461 }, 462 src: &struct{ S map[string]string }{ 463 S: map[string]string{ 464 "key0": "src_value0", 465 "key1": "src_value1", 466 "key3": "src_value3", 467 }, 468 }, 469 out: &struct{ S map[string]string }{ 470 S: map[string]string{ 471 "key0": "src_value0", 472 "key1": "src_value1", 473 "key2": "dst_value2", 474 "key3": "src_value3", 475 }, 476 }, 477 order: Append, 478 }, 479 { 480 name: "Prepend map", 481 dst: &struct{ S map[string]string }{ 482 S: map[string]string{ 483 "key0": "", 484 "key1": "dst_value1", 485 "key2": "dst_value2", 486 }, 487 }, 488 src: &struct{ S map[string]string }{ 489 S: map[string]string{ 490 "key0": "src_value0", 491 "key1": "src_value1", 492 "key3": "src_value3", 493 }, 494 }, 495 out: &struct{ S map[string]string }{ 496 S: map[string]string{ 497 "key0": "", 498 "key1": "dst_value1", 499 "key2": "dst_value2", 500 "key3": "src_value3", 501 }, 502 }, 503 order: Prepend, 504 }, 505 { 506 name: "Replace map", 507 dst: &struct{ S map[string]string }{ 508 S: map[string]string{ 509 "key0": "", 510 "key1": "dst_value1", 511 "key2": "dst_value2", 512 }, 513 }, 514 src: &struct{ S map[string]string }{ 515 S: map[string]string{ 516 "key0": "src_value0", 517 "key1": "src_value1", 518 "key3": "src_value3", 519 }, 520 }, 521 out: &struct{ S map[string]string }{ 522 S: map[string]string{ 523 "key0": "src_value0", 524 "key1": "src_value1", 525 "key3": "src_value3", 526 }, 527 }, 528 order: Replace, 529 }, 530 { 531 name: "Append empty map", 532 dst: &struct{ S1, S2 map[string]string }{ 533 S1: map[string]string{"key0": "dst_value0"}, 534 S2: map[string]string{}, 535 }, 536 src: &struct{ S1, S2 map[string]string }{ 537 S1: map[string]string{}, 538 S2: map[string]string{"key0": "src_value0"}, 539 }, 540 out: &struct{ S1, S2 map[string]string }{ 541 S1: map[string]string{"key0": "dst_value0"}, 542 S2: map[string]string{"key0": "src_value0"}, 543 }, 544 order: Append, 545 }, 546 { 547 name: "Prepend empty map", 548 dst: &struct{ S1, S2 map[string]string }{ 549 S1: map[string]string{"key0": "dst_value0"}, 550 S2: map[string]string{}, 551 }, 552 src: &struct{ S1, S2 map[string]string }{ 553 S1: map[string]string{}, 554 S2: map[string]string{"key0": "src_value0"}, 555 }, 556 out: &struct{ S1, S2 map[string]string }{ 557 S1: map[string]string{"key0": "dst_value0"}, 558 S2: map[string]string{"key0": "src_value0"}, 559 }, 560 order: Prepend, 561 }, 562 { 563 name: "Replace empty map", 564 dst: &struct{ S1, S2 map[string]string }{ 565 S1: map[string]string{"key0": "dst_value0"}, 566 S2: map[string]string{}, 567 }, 568 src: &struct{ S1, S2 map[string]string }{ 569 S1: map[string]string{}, 570 S2: map[string]string{"key0": "src_value0"}, 571 }, 572 out: &struct{ S1, S2 map[string]string }{ 573 S1: map[string]string{}, 574 S2: map[string]string{"key0": "src_value0"}, 575 }, 576 order: Replace, 577 }, 578 { 579 name: "Append nil map", 580 dst: &struct{ S1, S2, S3 map[string]string }{ 581 S1: map[string]string{"key0": "dst_value0"}, 582 }, 583 src: &struct{ S1, S2, S3 map[string]string }{ 584 S2: map[string]string{"key0": "src_value0"}, 585 }, 586 out: &struct{ S1, S2, S3 map[string]string }{ 587 S1: map[string]string{"key0": "dst_value0"}, 588 S2: map[string]string{"key0": "src_value0"}, 589 }, 590 order: Append, 591 }, 592 { 593 name: "Prepend nil map", 594 dst: &struct{ S1, S2, S3 map[string]string }{ 595 S1: map[string]string{"key0": "dst_value0"}, 596 }, 597 src: &struct{ S1, S2, S3 map[string]string }{ 598 S2: map[string]string{"key0": "src_value0"}, 599 }, 600 out: &struct{ S1, S2, S3 map[string]string }{ 601 S1: map[string]string{"key0": "dst_value0"}, 602 S2: map[string]string{"key0": "src_value0"}, 603 }, 604 order: Prepend, 605 }, 606 { 607 name: "Replace nil map", 608 dst: &struct{ S1, S2, S3 map[string]string }{ 609 S1: map[string]string{"key0": "dst_value0"}, 610 }, 611 src: &struct{ S1, S2, S3 map[string]string }{ 612 S2: map[string]string{"key0": "src_value0"}, 613 }, 614 out: &struct{ S1, S2, S3 map[string]string }{ 615 S1: map[string]string{"key0": "dst_value0"}, 616 S2: map[string]string{"key0": "src_value0"}, 617 S3: nil, 618 }, 619 order: Replace, 620 }, 621 { 622 name: "Replace slice of structs", 623 dst: &struct{ S []struct{ F string } }{ 624 S: []struct{ F string }{ 625 {F: "foo"}, {F: "bar"}, 626 }, 627 }, 628 src: &struct{ S []struct{ F string } }{ 629 S: []struct{ F string }{ 630 {F: "baz"}, 631 }, 632 }, 633 out: &struct{ S []struct{ F string } }{ 634 S: []struct{ F string }{ 635 {F: "baz"}, 636 }, 637 }, 638 order: Replace, 639 }, 640 { 641 name: "Append pointer", 642 dst: &struct{ S *struct{ S string } }{ 643 S: &struct{ S string }{ 644 S: "string1", 645 }, 646 }, 647 src: &struct{ S *struct{ S string } }{ 648 S: &struct{ S string }{ 649 S: "string2", 650 }, 651 }, 652 out: &struct{ S *struct{ S string } }{ 653 S: &struct{ S string }{ 654 S: "string1string2", 655 }, 656 }, 657 }, 658 { 659 name: "Prepend pointer", 660 dst: &struct{ S *struct{ S string } }{ 661 S: &struct{ S string }{ 662 S: "string1", 663 }, 664 }, 665 src: &struct{ S *struct{ S string } }{ 666 S: &struct{ S string }{ 667 S: "string2", 668 }, 669 }, 670 out: &struct{ S *struct{ S string } }{ 671 S: &struct{ S string }{ 672 S: "string2string1", 673 }, 674 }, 675 order: Prepend, 676 }, 677 { 678 name: "Append interface", 679 dst: &struct{ S interface{} }{ 680 S: &struct{ S string }{ 681 S: "string1", 682 }, 683 }, 684 src: &struct{ S interface{} }{ 685 S: &struct{ S string }{ 686 S: "string2", 687 }, 688 }, 689 out: &struct{ S interface{} }{ 690 S: &struct{ S string }{ 691 S: "string1string2", 692 }, 693 }, 694 }, 695 { 696 name: "Prepend interface", 697 dst: &struct{ S interface{} }{ 698 S: &struct{ S string }{ 699 S: "string1", 700 }, 701 }, 702 src: &struct{ S interface{} }{ 703 S: &struct{ S string }{ 704 S: "string2", 705 }, 706 }, 707 out: &struct{ S interface{} }{ 708 S: &struct{ S string }{ 709 S: "string2string1", 710 }, 711 }, 712 order: Prepend, 713 }, 714 { 715 name: "Unexported field", 716 dst: &struct{ s string }{ 717 s: "string1", 718 }, 719 src: &struct{ s string }{ 720 s: "string2", 721 }, 722 out: &struct{ s string }{ 723 s: "string1", 724 }, 725 }, 726 { 727 name: "Unexported field", 728 dst: &struct{ i *int64 }{ 729 i: Int64Ptr(33), 730 }, 731 src: &struct{ i *int64 }{ 732 i: Int64Ptr(5), 733 }, 734 out: &struct{ i *int64 }{ 735 i: Int64Ptr(33), 736 }, 737 }, 738 { 739 name: "Empty struct", 740 dst: &struct{}{}, 741 src: &struct{}{}, 742 out: &struct{}{}, 743 }, 744 { 745 name: "Interface nil", 746 dst: &struct{ S interface{} }{ 747 S: nil, 748 }, 749 src: &struct{ S interface{} }{ 750 S: nil, 751 }, 752 out: &struct{ S interface{} }{ 753 S: nil, 754 }, 755 }, 756 { 757 name: "Pointer nil", 758 dst: &struct{ S *struct{} }{ 759 S: nil, 760 }, 761 src: &struct{ S *struct{} }{ 762 S: nil, 763 }, 764 out: &struct{ S *struct{} }{ 765 S: nil, 766 }, 767 }, 768 { 769 name: "Anonymous struct", 770 dst: &struct { 771 EmbeddedStruct 772 Nested struct{ EmbeddedStruct } 773 }{ 774 EmbeddedStruct: EmbeddedStruct{ 775 S: "string1", 776 I: Int64Ptr(55), 777 }, 778 Nested: struct{ EmbeddedStruct }{ 779 EmbeddedStruct: EmbeddedStruct{ 780 S: "string2", 781 I: Int64Ptr(-4), 782 }, 783 }, 784 }, 785 src: &struct { 786 EmbeddedStruct 787 Nested struct{ EmbeddedStruct } 788 }{ 789 EmbeddedStruct: EmbeddedStruct{ 790 S: "string3", 791 I: Int64Ptr(66), 792 }, 793 Nested: struct{ EmbeddedStruct }{ 794 EmbeddedStruct: EmbeddedStruct{ 795 S: "string4", 796 I: Int64Ptr(-8), 797 }, 798 }, 799 }, 800 out: &struct { 801 EmbeddedStruct 802 Nested struct{ EmbeddedStruct } 803 }{ 804 EmbeddedStruct: EmbeddedStruct{ 805 S: "string1string3", 806 I: Int64Ptr(66), 807 }, 808 Nested: struct{ EmbeddedStruct }{ 809 EmbeddedStruct: EmbeddedStruct{ 810 S: "string2string4", 811 I: Int64Ptr(-8), 812 }, 813 }, 814 }, 815 }, 816 { 817 name: "BlueprintEmbed struct", 818 dst: &struct { 819 BlueprintEmbed EmbeddedStruct 820 Nested struct{ BlueprintEmbed EmbeddedStruct } 821 }{ 822 BlueprintEmbed: EmbeddedStruct{ 823 S: "string1", 824 I: Int64Ptr(55), 825 }, 826 Nested: struct{ BlueprintEmbed EmbeddedStruct }{ 827 BlueprintEmbed: EmbeddedStruct{ 828 S: "string2", 829 I: Int64Ptr(-4), 830 }, 831 }, 832 }, 833 src: &struct { 834 BlueprintEmbed EmbeddedStruct 835 Nested struct{ BlueprintEmbed EmbeddedStruct } 836 }{ 837 BlueprintEmbed: EmbeddedStruct{ 838 S: "string3", 839 I: Int64Ptr(66), 840 }, 841 Nested: struct{ BlueprintEmbed EmbeddedStruct }{ 842 BlueprintEmbed: EmbeddedStruct{ 843 S: "string4", 844 I: Int64Ptr(-8), 845 }, 846 }, 847 }, 848 out: &struct { 849 BlueprintEmbed EmbeddedStruct 850 Nested struct{ BlueprintEmbed EmbeddedStruct } 851 }{ 852 BlueprintEmbed: EmbeddedStruct{ 853 S: "string1string3", 854 I: Int64Ptr(66), 855 }, 856 Nested: struct{ BlueprintEmbed EmbeddedStruct }{ 857 BlueprintEmbed: EmbeddedStruct{ 858 S: "string2string4", 859 I: Int64Ptr(-8), 860 }, 861 }, 862 }, 863 }, 864 { 865 name: "Anonymous interface", 866 dst: &struct { 867 EmbeddedInterface 868 Nested struct{ EmbeddedInterface } 869 }{ 870 EmbeddedInterface: &struct { 871 S string 872 I *int64 873 }{ 874 S: "string1", 875 I: Int64Ptr(-8), 876 }, 877 Nested: struct{ EmbeddedInterface }{ 878 EmbeddedInterface: &struct { 879 S string 880 I *int64 881 }{ 882 S: "string2", 883 I: Int64Ptr(55), 884 }, 885 }, 886 }, 887 src: &struct { 888 EmbeddedInterface 889 Nested struct{ EmbeddedInterface } 890 }{ 891 EmbeddedInterface: &struct { 892 S string 893 I *int64 894 }{ 895 S: "string3", 896 I: Int64Ptr(6), 897 }, 898 Nested: struct{ EmbeddedInterface }{ 899 EmbeddedInterface: &struct { 900 S string 901 I *int64 902 }{ 903 S: "string4", 904 I: Int64Ptr(6), 905 }, 906 }, 907 }, 908 out: &struct { 909 EmbeddedInterface 910 Nested struct{ EmbeddedInterface } 911 }{ 912 EmbeddedInterface: &struct { 913 S string 914 I *int64 915 }{ 916 S: "string1string3", 917 I: Int64Ptr(6), 918 }, 919 Nested: struct{ EmbeddedInterface }{ 920 EmbeddedInterface: &struct { 921 S string 922 I *int64 923 }{ 924 S: "string2string4", 925 I: Int64Ptr(6), 926 }, 927 }, 928 }, 929 }, 930 { 931 name: "Nil pointer to a struct", 932 dst: &struct { 933 Nested *struct { 934 S string 935 } 936 }{}, 937 src: &struct { 938 Nested *struct { 939 S string 940 } 941 }{ 942 Nested: &struct { 943 S string 944 }{ 945 S: "string", 946 }, 947 }, 948 out: &struct { 949 Nested *struct { 950 S string 951 } 952 }{ 953 Nested: &struct { 954 S string 955 }{ 956 S: "string", 957 }, 958 }, 959 }, 960 { 961 name: "Nil pointer to a struct in an interface", 962 dst: &struct { 963 Nested interface{} 964 }{ 965 Nested: (*struct{ S string })(nil), 966 }, 967 src: &struct { 968 Nested interface{} 969 }{ 970 Nested: &struct { 971 S string 972 }{ 973 S: "string", 974 }, 975 }, 976 out: &struct { 977 Nested interface{} 978 }{ 979 Nested: &struct { 980 S string 981 }{ 982 S: "string", 983 }, 984 }, 985 }, 986 { 987 name: "Interface src nil", 988 dst: &struct{ S interface{} }{ 989 S: &struct{ S string }{ 990 S: "string1", 991 }, 992 }, 993 src: &struct{ S interface{} }{ 994 S: nil, 995 }, 996 out: &struct{ S interface{} }{ 997 S: &struct{ S string }{ 998 S: "string1", 999 }, 1000 }, 1001 }, 1002 1003 // Errors 1004 1005 { 1006 name: "Non-pointer dst", 1007 dst: struct{}{}, 1008 src: &struct{}{}, 1009 err: errors.New("expected pointer to struct, got struct {}"), 1010 out: struct{}{}, 1011 }, 1012 { 1013 name: "Non-pointer src", 1014 dst: &struct{}{}, 1015 src: struct{}{}, 1016 err: errors.New("expected pointer to struct, got struct {}"), 1017 out: &struct{}{}, 1018 }, 1019 { 1020 name: "Non-struct dst", 1021 dst: &[]string{"bad"}, 1022 src: &struct{}{}, 1023 err: errors.New("expected pointer to struct, got *[]string"), 1024 out: &[]string{"bad"}, 1025 }, 1026 { 1027 name: "Non-struct src", 1028 dst: &struct{}{}, 1029 src: &[]string{"bad"}, 1030 err: errors.New("expected pointer to struct, got *[]string"), 1031 out: &struct{}{}, 1032 }, 1033 { 1034 name: "Mismatched types", 1035 dst: &struct{ A string }{ 1036 A: "string1", 1037 }, 1038 src: &struct{ B string }{ 1039 B: "string2", 1040 }, 1041 out: &struct{ A string }{ 1042 A: "string1", 1043 }, 1044 err: errors.New("expected matching types for dst and src, got *struct { A string } and *struct { B string }"), 1045 }, 1046 { 1047 name: "Unsupported kind", 1048 dst: &struct{ I int }{ 1049 I: 1, 1050 }, 1051 src: &struct{ I int }{ 1052 I: 2, 1053 }, 1054 out: &struct{ I int }{ 1055 I: 1, 1056 }, 1057 err: extendPropertyErrorf("i", "unsupported kind int"), 1058 }, 1059 { 1060 name: "Unsupported kind", 1061 dst: &struct{ I int64 }{ 1062 I: 1, 1063 }, 1064 src: &struct{ I int64 }{ 1065 I: 2, 1066 }, 1067 out: &struct{ I int64 }{ 1068 I: 1, 1069 }, 1070 err: extendPropertyErrorf("i", "unsupported kind int64"), 1071 }, 1072 { 1073 name: "Interface nilitude mismatch", 1074 dst: &struct{ S interface{} }{ 1075 S: nil, 1076 }, 1077 src: &struct{ S interface{} }{ 1078 S: &struct{ S string }{ 1079 S: "string1", 1080 }, 1081 }, 1082 out: &struct{ S interface{} }{ 1083 S: nil, 1084 }, 1085 err: extendPropertyErrorf("s", "nilitude mismatch"), 1086 }, 1087 { 1088 name: "Interface type mismatch", 1089 dst: &struct{ S interface{} }{ 1090 S: &struct{ A string }{ 1091 A: "string1", 1092 }, 1093 }, 1094 src: &struct{ S interface{} }{ 1095 S: &struct{ B string }{ 1096 B: "string2", 1097 }, 1098 }, 1099 out: &struct{ S interface{} }{ 1100 S: &struct{ A string }{ 1101 A: "string1", 1102 }, 1103 }, 1104 err: extendPropertyErrorf("s", "mismatched types struct { A string } and struct { B string }"), 1105 }, 1106 { 1107 name: "Interface not a pointer", 1108 dst: &struct{ S interface{} }{ 1109 S: struct{ S string }{ 1110 S: "string1", 1111 }, 1112 }, 1113 src: &struct{ S interface{} }{ 1114 S: struct{ S string }{ 1115 S: "string2", 1116 }, 1117 }, 1118 out: &struct{ S interface{} }{ 1119 S: struct{ S string }{ 1120 S: "string1", 1121 }, 1122 }, 1123 err: extendPropertyErrorf("s", "interface not a pointer"), 1124 }, 1125 { 1126 name: "Pointer not a struct", 1127 dst: &struct{ S *[]string }{ 1128 S: &[]string{"string1"}, 1129 }, 1130 src: &struct{ S *[]string }{ 1131 S: &[]string{"string2"}, 1132 }, 1133 out: &struct{ S *[]string }{ 1134 S: &[]string{"string1"}, 1135 }, 1136 err: extendPropertyErrorf("s", "pointer is a slice"), 1137 }, 1138 { 1139 name: "Error in nested struct", 1140 dst: &struct{ S interface{} }{ 1141 S: &struct{ I int }{ 1142 I: 1, 1143 }, 1144 }, 1145 src: &struct{ S interface{} }{ 1146 S: &struct{ I int }{ 1147 I: 2, 1148 }, 1149 }, 1150 out: &struct{ S interface{} }{ 1151 S: &struct{ I int }{ 1152 I: 1, 1153 }, 1154 }, 1155 err: extendPropertyErrorf("s.i", "unsupported kind int"), 1156 }, 1157 1158 // Filters 1159 1160 { 1161 name: "Filter true", 1162 dst: &struct{ S string }{ 1163 S: "string1", 1164 }, 1165 src: &struct{ S string }{ 1166 S: "string2", 1167 }, 1168 out: &struct{ S string }{ 1169 S: "string1string2", 1170 }, 1171 filter: func(dstField, srcField reflect.StructField) (bool, error) { 1172 return true, nil 1173 }, 1174 }, 1175 { 1176 name: "Filter false", 1177 dst: &struct{ S string }{ 1178 S: "string1", 1179 }, 1180 src: &struct{ S string }{ 1181 S: "string2", 1182 }, 1183 out: &struct{ S string }{ 1184 S: "string1", 1185 }, 1186 filter: func(dstField, srcField reflect.StructField) (bool, error) { 1187 return false, nil 1188 }, 1189 }, 1190 { 1191 name: "Filter check args", 1192 dst: &struct{ S string }{ 1193 S: "string1", 1194 }, 1195 src: &struct{ S string }{ 1196 S: "string2", 1197 }, 1198 out: &struct{ S string }{ 1199 S: "string1string2", 1200 }, 1201 filter: func(dstField, srcField reflect.StructField) (bool, error) { 1202 return dstField.Name == "S" && srcField.Name == "S", nil 1203 }, 1204 }, 1205 { 1206 name: "Filter mutated", 1207 dst: &struct { 1208 S string `blueprint:"mutated"` 1209 }{ 1210 S: "string1", 1211 }, 1212 src: &struct { 1213 S string `blueprint:"mutated"` 1214 }{ 1215 S: "string2", 1216 }, 1217 out: &struct { 1218 S string `blueprint:"mutated"` 1219 }{ 1220 S: "string1", 1221 }, 1222 }, 1223 { 1224 name: "Filter mutated", 1225 dst: &struct { 1226 S *int64 `blueprint:"mutated"` 1227 }{ 1228 S: Int64Ptr(4), 1229 }, 1230 src: &struct { 1231 S *int64 `blueprint:"mutated"` 1232 }{ 1233 S: Int64Ptr(5), 1234 }, 1235 out: &struct { 1236 S *int64 `blueprint:"mutated"` 1237 }{ 1238 S: Int64Ptr(4), 1239 }, 1240 }, 1241 { 1242 name: "Filter error", 1243 dst: &struct{ S string }{ 1244 S: "string1", 1245 }, 1246 src: &struct{ S string }{ 1247 S: "string2", 1248 }, 1249 out: &struct{ S string }{ 1250 S: "string1", 1251 }, 1252 filter: func(dstField, srcField reflect.StructField) (bool, error) { 1253 return true, fmt.Errorf("filter error") 1254 }, 1255 err: extendPropertyErrorf("s", "filter error"), 1256 }, 1257 { 1258 name: "Append configurable", 1259 dst: &struct{ S Configurable[[]string] }{ 1260 S: Configurable[[]string]{ 1261 inner: &configurableInner[[]string]{ 1262 single: singleConfigurable[[]string]{ 1263 conditions: []ConfigurableCondition{{ 1264 functionName: "soong_config_variable", 1265 args: []string{ 1266 "my_namespace", 1267 "foo", 1268 }, 1269 }}, 1270 cases: []ConfigurableCase[[]string]{{ 1271 patterns: []ConfigurablePattern{{ 1272 typ: configurablePatternTypeString, 1273 stringValue: "a", 1274 }}, 1275 value: &[]string{"1", "2"}, 1276 }}, 1277 }, 1278 }, 1279 }, 1280 }, 1281 src: &struct{ S Configurable[[]string] }{ 1282 S: Configurable[[]string]{ 1283 inner: &configurableInner[[]string]{ 1284 single: singleConfigurable[[]string]{ 1285 conditions: []ConfigurableCondition{{ 1286 functionName: "release_variable", 1287 args: []string{ 1288 "bar", 1289 }, 1290 }}, 1291 cases: []ConfigurableCase[[]string]{{ 1292 patterns: []ConfigurablePattern{{ 1293 typ: configurablePatternTypeString, 1294 stringValue: "b", 1295 }}, 1296 value: &[]string{"3", "4"}, 1297 }}, 1298 }, 1299 }, 1300 }, 1301 }, 1302 out: &struct{ S Configurable[[]string] }{ 1303 S: Configurable[[]string]{ 1304 inner: &configurableInner[[]string]{ 1305 single: singleConfigurable[[]string]{ 1306 conditions: []ConfigurableCondition{{ 1307 functionName: "soong_config_variable", 1308 args: []string{ 1309 "my_namespace", 1310 "foo", 1311 }, 1312 }}, 1313 cases: []ConfigurableCase[[]string]{{ 1314 patterns: []ConfigurablePattern{{ 1315 typ: configurablePatternTypeString, 1316 stringValue: "a", 1317 }}, 1318 value: &[]string{"1", "2"}, 1319 }}, 1320 }, 1321 next: &configurableInner[[]string]{ 1322 single: singleConfigurable[[]string]{ 1323 conditions: []ConfigurableCondition{{ 1324 functionName: "release_variable", 1325 args: []string{ 1326 "bar", 1327 }, 1328 }}, 1329 cases: []ConfigurableCase[[]string]{{ 1330 patterns: []ConfigurablePattern{{ 1331 typ: configurablePatternTypeString, 1332 stringValue: "b", 1333 }}, 1334 value: &[]string{"3", "4"}, 1335 }}, 1336 }, 1337 }, 1338 }, 1339 }, 1340 }, 1341 }, 1342 { 1343 name: "Prepend configurable", 1344 order: Prepend, 1345 dst: &struct{ S Configurable[[]string] }{ 1346 S: Configurable[[]string]{ 1347 inner: &configurableInner[[]string]{ 1348 single: singleConfigurable[[]string]{ 1349 conditions: []ConfigurableCondition{{ 1350 functionName: "soong_config_variable", 1351 args: []string{ 1352 "my_namespace", 1353 "foo", 1354 }, 1355 }}, 1356 cases: []ConfigurableCase[[]string]{{ 1357 patterns: []ConfigurablePattern{{ 1358 typ: configurablePatternTypeString, 1359 stringValue: "a", 1360 }}, 1361 value: &[]string{"1", "2"}, 1362 }}, 1363 }, 1364 }, 1365 }, 1366 }, 1367 src: &struct{ S Configurable[[]string] }{ 1368 S: Configurable[[]string]{ 1369 inner: &configurableInner[[]string]{ 1370 single: singleConfigurable[[]string]{ 1371 conditions: []ConfigurableCondition{{ 1372 functionName: "release_variable", 1373 args: []string{ 1374 "bar", 1375 }, 1376 }}, 1377 cases: []ConfigurableCase[[]string]{{ 1378 patterns: []ConfigurablePattern{{ 1379 typ: configurablePatternTypeString, 1380 stringValue: "b", 1381 }}, 1382 value: &[]string{"3", "4"}, 1383 }}, 1384 }, 1385 }, 1386 }, 1387 }, 1388 out: &struct{ S Configurable[[]string] }{ 1389 S: Configurable[[]string]{ 1390 inner: &configurableInner[[]string]{ 1391 single: singleConfigurable[[]string]{ 1392 conditions: []ConfigurableCondition{{ 1393 functionName: "release_variable", 1394 args: []string{ 1395 "bar", 1396 }, 1397 }}, 1398 cases: []ConfigurableCase[[]string]{{ 1399 patterns: []ConfigurablePattern{{ 1400 typ: configurablePatternTypeString, 1401 stringValue: "b", 1402 }}, 1403 value: &[]string{"3", "4"}, 1404 }}, 1405 }, 1406 next: &configurableInner[[]string]{ 1407 single: singleConfigurable[[]string]{ 1408 conditions: []ConfigurableCondition{{ 1409 functionName: "soong_config_variable", 1410 args: []string{ 1411 "my_namespace", 1412 "foo", 1413 }, 1414 }}, 1415 cases: []ConfigurableCase[[]string]{{ 1416 patterns: []ConfigurablePattern{{ 1417 typ: configurablePatternTypeString, 1418 stringValue: "a", 1419 }}, 1420 value: &[]string{"1", "2"}, 1421 }}, 1422 }, 1423 }, 1424 }, 1425 }, 1426 }, 1427 }, 1428 } 1429} 1430 1431func TestAppendProperties(t *testing.T) { 1432 for _, testCase := range appendPropertiesTestCases() { 1433 t.Run(testCase.name, func(t *testing.T) { 1434 1435 got := testCase.dst 1436 var err error 1437 var testType string 1438 1439 switch testCase.order { 1440 case Append: 1441 testType = "append" 1442 err = AppendProperties(got, testCase.src, testCase.filter) 1443 case Prepend: 1444 testType = "prepend" 1445 err = PrependProperties(got, testCase.src, testCase.filter) 1446 case Replace: 1447 testType = "replace" 1448 err = ExtendProperties(got, testCase.src, testCase.filter, OrderReplace) 1449 } 1450 1451 check(t, testType, testCase.name, got, err, testCase.out, testCase.err) 1452 }) 1453 } 1454} 1455 1456func TestExtendProperties(t *testing.T) { 1457 for _, testCase := range appendPropertiesTestCases() { 1458 t.Run(testCase.name, func(t *testing.T) { 1459 1460 got := testCase.dst 1461 var err error 1462 var testType string 1463 1464 order := func(dstField, srcField reflect.StructField) (Order, error) { 1465 switch testCase.order { 1466 case Append: 1467 return Append, nil 1468 case Prepend: 1469 return Prepend, nil 1470 case Replace: 1471 return Replace, nil 1472 } 1473 return Append, errors.New("unknown order") 1474 } 1475 1476 switch testCase.order { 1477 case Append: 1478 testType = "prepend" 1479 case Prepend: 1480 testType = "append" 1481 case Replace: 1482 testType = "replace" 1483 } 1484 1485 err = ExtendProperties(got, testCase.src, testCase.filter, order) 1486 1487 check(t, testType, testCase.name, got, err, testCase.out, testCase.err) 1488 }) 1489 } 1490} 1491 1492type appendMatchingPropertiesTestCase struct { 1493 name string 1494 dst []interface{} 1495 src interface{} 1496 out []interface{} 1497 order Order // default is Append 1498 filter ExtendPropertyFilterFunc 1499 err error 1500} 1501 1502func appendMatchingPropertiesTestCases() []appendMatchingPropertiesTestCase { 1503 return []appendMatchingPropertiesTestCase{ 1504 { 1505 name: "Append strings", 1506 dst: []interface{}{&struct{ S string }{ 1507 S: "string1", 1508 }}, 1509 src: &struct{ S string }{ 1510 S: "string2", 1511 }, 1512 out: []interface{}{&struct{ S string }{ 1513 S: "string1string2", 1514 }}, 1515 }, 1516 { 1517 name: "Prepend strings", 1518 dst: []interface{}{&struct{ S string }{ 1519 S: "string1", 1520 }}, 1521 src: &struct{ S string }{ 1522 S: "string2", 1523 }, 1524 out: []interface{}{&struct{ S string }{ 1525 S: "string2string1", 1526 }}, 1527 order: Prepend, 1528 }, 1529 { 1530 name: "Append all", 1531 dst: []interface{}{ 1532 &struct{ S, A string }{ 1533 S: "string1", 1534 }, 1535 &struct{ S, B string }{ 1536 S: "string2", 1537 }, 1538 }, 1539 src: &struct{ S string }{ 1540 S: "string3", 1541 }, 1542 out: []interface{}{ 1543 &struct{ S, A string }{ 1544 S: "string1string3", 1545 }, 1546 &struct{ S, B string }{ 1547 S: "string2string3", 1548 }, 1549 }, 1550 }, 1551 { 1552 name: "Append some", 1553 dst: []interface{}{ 1554 &struct{ S, A string }{ 1555 S: "string1", 1556 }, 1557 &struct{ B string }{}, 1558 }, 1559 src: &struct{ S string }{ 1560 S: "string2", 1561 }, 1562 out: []interface{}{ 1563 &struct{ S, A string }{ 1564 S: "string1string2", 1565 }, 1566 &struct{ B string }{}, 1567 }, 1568 }, 1569 { 1570 name: "Append mismatched structs", 1571 dst: []interface{}{&struct{ S, A string }{ 1572 S: "string1", 1573 }}, 1574 src: &struct{ S string }{ 1575 S: "string2", 1576 }, 1577 out: []interface{}{&struct{ S, A string }{ 1578 S: "string1string2", 1579 }}, 1580 }, 1581 { 1582 name: "Append mismatched pointer structs", 1583 dst: []interface{}{&struct{ S *struct{ S, A string } }{ 1584 S: &struct{ S, A string }{ 1585 S: "string1", 1586 }, 1587 }}, 1588 src: &struct{ S *struct{ S string } }{ 1589 S: &struct{ S string }{ 1590 S: "string2", 1591 }, 1592 }, 1593 out: []interface{}{&struct{ S *struct{ S, A string } }{ 1594 S: &struct{ S, A string }{ 1595 S: "string1string2", 1596 }, 1597 }}, 1598 }, 1599 { 1600 name: "Append through mismatched types", 1601 dst: []interface{}{ 1602 &struct{ B string }{}, 1603 &struct{ S interface{} }{ 1604 S: &struct{ S, A string }{ 1605 S: "string1", 1606 }, 1607 }, 1608 }, 1609 src: &struct{ S struct{ S string } }{ 1610 S: struct{ S string }{ 1611 S: "string2", 1612 }, 1613 }, 1614 out: []interface{}{ 1615 &struct{ B string }{}, 1616 &struct{ S interface{} }{ 1617 S: &struct{ S, A string }{ 1618 S: "string1string2", 1619 }, 1620 }, 1621 }, 1622 }, 1623 { 1624 name: "Append through mismatched types and nil", 1625 dst: []interface{}{ 1626 &struct{ B string }{}, 1627 &struct{ S interface{} }{ 1628 S: (*struct{ S, A string })(nil), 1629 }, 1630 }, 1631 src: &struct{ S struct{ S string } }{ 1632 S: struct{ S string }{ 1633 S: "string2", 1634 }, 1635 }, 1636 out: []interface{}{ 1637 &struct{ B string }{}, 1638 &struct{ S interface{} }{ 1639 S: &struct{ S, A string }{ 1640 S: "string2", 1641 }, 1642 }, 1643 }, 1644 }, 1645 { 1646 name: "Append through multiple matches", 1647 dst: []interface{}{ 1648 &struct { 1649 S struct{ S, A string } 1650 }{ 1651 S: struct{ S, A string }{ 1652 S: "string1", 1653 }, 1654 }, 1655 &struct { 1656 S struct{ S, B string } 1657 }{ 1658 S: struct{ S, B string }{ 1659 S: "string2", 1660 }, 1661 }, 1662 }, 1663 src: &struct{ S struct{ B string } }{ 1664 S: struct{ B string }{ 1665 B: "string3", 1666 }, 1667 }, 1668 out: []interface{}{ 1669 &struct { 1670 S struct{ S, A string } 1671 }{ 1672 S: struct{ S, A string }{ 1673 S: "string1", 1674 }, 1675 }, 1676 &struct { 1677 S struct{ S, B string } 1678 }{ 1679 S: struct{ S, B string }{ 1680 S: "string2", 1681 B: "string3", 1682 }, 1683 }, 1684 }, 1685 }, 1686 { 1687 name: "Append through embedded struct", 1688 dst: []interface{}{ 1689 &struct{ B string }{}, 1690 &struct{ EmbeddedStruct }{ 1691 EmbeddedStruct: EmbeddedStruct{ 1692 S: "string1", 1693 }, 1694 }, 1695 }, 1696 src: &struct{ S string }{ 1697 S: "string2", 1698 }, 1699 out: []interface{}{ 1700 &struct{ B string }{}, 1701 &struct{ EmbeddedStruct }{ 1702 EmbeddedStruct: EmbeddedStruct{ 1703 S: "string1string2", 1704 }, 1705 }, 1706 }, 1707 }, 1708 { 1709 name: "Append through BlueprintEmbed struct", 1710 dst: []interface{}{ 1711 &struct{ B string }{}, 1712 &struct{ BlueprintEmbed EmbeddedStruct }{ 1713 BlueprintEmbed: EmbeddedStruct{ 1714 S: "string1", 1715 }, 1716 }, 1717 }, 1718 src: &struct{ S string }{ 1719 S: "string2", 1720 }, 1721 out: []interface{}{ 1722 &struct{ B string }{}, 1723 &struct{ BlueprintEmbed EmbeddedStruct }{ 1724 BlueprintEmbed: EmbeddedStruct{ 1725 S: "string1string2", 1726 }, 1727 }, 1728 }, 1729 }, 1730 { 1731 name: "Append through embedded pointer to struct", 1732 dst: []interface{}{ 1733 &struct{ B string }{}, 1734 &struct{ *EmbeddedStruct }{ 1735 EmbeddedStruct: &EmbeddedStruct{ 1736 S: "string1", 1737 }, 1738 }, 1739 }, 1740 src: &struct{ S string }{ 1741 S: "string2", 1742 }, 1743 out: []interface{}{ 1744 &struct{ B string }{}, 1745 &struct{ *EmbeddedStruct }{ 1746 EmbeddedStruct: &EmbeddedStruct{ 1747 S: "string1string2", 1748 }, 1749 }, 1750 }, 1751 }, 1752 { 1753 name: "Append through BlueprintEmbed pointer to struct", 1754 dst: []interface{}{ 1755 &struct{ B string }{}, 1756 &struct{ BlueprintEmbed *EmbeddedStruct }{ 1757 BlueprintEmbed: &EmbeddedStruct{ 1758 S: "string1", 1759 }, 1760 }, 1761 }, 1762 src: &struct{ S string }{ 1763 S: "string2", 1764 }, 1765 out: []interface{}{ 1766 &struct{ B string }{}, 1767 &struct{ BlueprintEmbed *EmbeddedStruct }{ 1768 BlueprintEmbed: &EmbeddedStruct{ 1769 S: "string1string2", 1770 }, 1771 }, 1772 }, 1773 }, 1774 { 1775 name: "Append through embedded nil pointer to struct", 1776 dst: []interface{}{ 1777 &struct{ B string }{}, 1778 &struct{ *EmbeddedStruct }{}, 1779 }, 1780 src: &struct{ S string }{ 1781 S: "string2", 1782 }, 1783 out: []interface{}{ 1784 &struct{ B string }{}, 1785 &struct{ *EmbeddedStruct }{ 1786 EmbeddedStruct: &EmbeddedStruct{ 1787 S: "string2", 1788 }, 1789 }, 1790 }, 1791 }, 1792 { 1793 name: "Append through BlueprintEmbed nil pointer to struct", 1794 dst: []interface{}{ 1795 &struct{ B string }{}, 1796 &struct{ BlueprintEmbed *EmbeddedStruct }{}, 1797 }, 1798 src: &struct{ S string }{ 1799 S: "string2", 1800 }, 1801 out: []interface{}{ 1802 &struct{ B string }{}, 1803 &struct{ BlueprintEmbed *EmbeddedStruct }{ 1804 BlueprintEmbed: &EmbeddedStruct{ 1805 S: "string2", 1806 }, 1807 }, 1808 }, 1809 }, 1810 1811 // Errors 1812 1813 { 1814 name: "Non-pointer dst", 1815 dst: []interface{}{struct{}{}}, 1816 src: &struct{}{}, 1817 err: errors.New("expected pointer to struct, got struct {}"), 1818 out: []interface{}{struct{}{}}, 1819 }, 1820 { 1821 name: "Non-pointer src", 1822 dst: []interface{}{&struct{}{}}, 1823 src: struct{}{}, 1824 err: errors.New("expected pointer to struct, got struct {}"), 1825 out: []interface{}{&struct{}{}}, 1826 }, 1827 { 1828 name: "Non-struct dst", 1829 dst: []interface{}{&[]string{"bad"}}, 1830 src: &struct{}{}, 1831 err: errors.New("expected pointer to struct, got *[]string"), 1832 out: []interface{}{&[]string{"bad"}}, 1833 }, 1834 { 1835 name: "Non-struct src", 1836 dst: []interface{}{&struct{}{}}, 1837 src: &[]string{"bad"}, 1838 err: errors.New("expected pointer to struct, got *[]string"), 1839 out: []interface{}{&struct{}{}}, 1840 }, 1841 { 1842 name: "Append none", 1843 dst: []interface{}{ 1844 &struct{ A string }{}, 1845 &struct{ B string }{}, 1846 }, 1847 src: &struct{ S string }{ 1848 S: "string1", 1849 }, 1850 out: []interface{}{ 1851 &struct{ A string }{}, 1852 &struct{ B string }{}, 1853 }, 1854 err: extendPropertyErrorf("s", "failed to find property to extend"), 1855 }, 1856 { 1857 name: "Append mismatched kinds", 1858 dst: []interface{}{ 1859 &struct{ S string }{ 1860 S: "string1", 1861 }, 1862 }, 1863 src: &struct{ S []string }{ 1864 S: []string{"string2"}, 1865 }, 1866 out: []interface{}{ 1867 &struct{ S string }{ 1868 S: "string1", 1869 }, 1870 }, 1871 err: extendPropertyErrorf("s", "mismatched types string and []string"), 1872 }, 1873 { 1874 name: "Append mismatched types", 1875 dst: []interface{}{ 1876 &struct{ S []int }{ 1877 S: []int{1}, 1878 }, 1879 }, 1880 src: &struct{ S []string }{ 1881 S: []string{"string2"}, 1882 }, 1883 out: []interface{}{ 1884 &struct{ S []int }{ 1885 S: []int{1}, 1886 }, 1887 }, 1888 err: extendPropertyErrorf("s", "mismatched types []int and []string"), 1889 }, 1890 { 1891 name: "Append *bool to Configurable[bool]", 1892 order: Append, 1893 dst: []interface{}{ 1894 &struct{ S Configurable[bool] }{ 1895 S: Configurable[bool]{ 1896 inner: &configurableInner[bool]{ 1897 single: singleConfigurable[bool]{ 1898 conditions: []ConfigurableCondition{{ 1899 functionName: "soong_config_variable", 1900 args: []string{ 1901 "my_namespace", 1902 "foo", 1903 }, 1904 }}, 1905 cases: []ConfigurableCase[bool]{{ 1906 patterns: []ConfigurablePattern{{ 1907 typ: configurablePatternTypeString, 1908 stringValue: "a", 1909 }}, 1910 value: BoolPtr(true), 1911 }, { 1912 patterns: []ConfigurablePattern{{ 1913 typ: configurablePatternTypeDefault, 1914 }}, 1915 value: BoolPtr(false), 1916 }}, 1917 }, 1918 }, 1919 }, 1920 }, 1921 }, 1922 src: &struct{ S *bool }{ 1923 S: BoolPtr(true), 1924 }, 1925 out: []interface{}{ 1926 &struct{ S Configurable[bool] }{ 1927 S: Configurable[bool]{ 1928 inner: &configurableInner[bool]{ 1929 single: singleConfigurable[bool]{ 1930 conditions: []ConfigurableCondition{{ 1931 functionName: "soong_config_variable", 1932 args: []string{ 1933 "my_namespace", 1934 "foo", 1935 }, 1936 }}, 1937 cases: []ConfigurableCase[bool]{{ 1938 patterns: []ConfigurablePattern{{ 1939 typ: configurablePatternTypeString, 1940 stringValue: "a", 1941 }}, 1942 value: BoolPtr(true), 1943 }, { 1944 patterns: []ConfigurablePattern{{ 1945 typ: configurablePatternTypeDefault, 1946 }}, 1947 value: BoolPtr(false), 1948 }}, 1949 }, 1950 next: &configurableInner[bool]{ 1951 single: singleConfigurable[bool]{ 1952 cases: []ConfigurableCase[bool]{{ 1953 value: BoolPtr(true), 1954 }}, 1955 }, 1956 }, 1957 }, 1958 }, 1959 }, 1960 }, 1961 }, 1962 { 1963 name: "Append bool to Configurable[bool]", 1964 order: Append, 1965 dst: []interface{}{ 1966 &struct{ S Configurable[bool] }{ 1967 S: Configurable[bool]{ 1968 inner: &configurableInner[bool]{ 1969 single: singleConfigurable[bool]{ 1970 conditions: []ConfigurableCondition{{ 1971 functionName: "soong_config_variable", 1972 args: []string{ 1973 "my_namespace", 1974 "foo", 1975 }, 1976 }}, 1977 cases: []ConfigurableCase[bool]{{ 1978 patterns: []ConfigurablePattern{{ 1979 typ: configurablePatternTypeString, 1980 stringValue: "a", 1981 }}, 1982 value: BoolPtr(true), 1983 }, { 1984 patterns: []ConfigurablePattern{{ 1985 typ: configurablePatternTypeDefault, 1986 }}, 1987 value: BoolPtr(false), 1988 }}, 1989 }, 1990 }, 1991 }, 1992 }, 1993 }, 1994 src: &struct{ S bool }{ 1995 S: true, 1996 }, 1997 out: []interface{}{ 1998 &struct{ S Configurable[bool] }{ 1999 S: Configurable[bool]{ 2000 inner: &configurableInner[bool]{ 2001 single: singleConfigurable[bool]{ 2002 conditions: []ConfigurableCondition{{ 2003 functionName: "soong_config_variable", 2004 args: []string{ 2005 "my_namespace", 2006 "foo", 2007 }, 2008 }}, 2009 cases: []ConfigurableCase[bool]{{ 2010 patterns: []ConfigurablePattern{{ 2011 typ: configurablePatternTypeString, 2012 stringValue: "a", 2013 }}, 2014 value: BoolPtr(true), 2015 }, { 2016 patterns: []ConfigurablePattern{{ 2017 typ: configurablePatternTypeDefault, 2018 }}, 2019 value: BoolPtr(false), 2020 }}, 2021 }, 2022 next: &configurableInner[bool]{ 2023 single: singleConfigurable[bool]{ 2024 cases: []ConfigurableCase[bool]{{ 2025 value: BoolPtr(true), 2026 }}, 2027 }, 2028 }, 2029 }, 2030 }, 2031 }, 2032 }, 2033 }, 2034 } 2035} 2036 2037func TestAppendMatchingProperties(t *testing.T) { 2038 for _, testCase := range appendMatchingPropertiesTestCases() { 2039 t.Run(testCase.name, func(t *testing.T) { 2040 2041 got := testCase.dst 2042 var err error 2043 var testType string 2044 2045 switch testCase.order { 2046 case Append: 2047 testType = "append" 2048 err = AppendMatchingProperties(got, testCase.src, testCase.filter) 2049 case Prepend: 2050 testType = "prepend" 2051 err = PrependMatchingProperties(got, testCase.src, testCase.filter) 2052 case Replace: 2053 testType = "replace" 2054 err = ExtendMatchingProperties(got, testCase.src, testCase.filter, OrderReplace) 2055 } 2056 2057 check(t, testType, testCase.name, got, err, testCase.out, testCase.err) 2058 }) 2059 } 2060} 2061 2062func TestExtendMatchingProperties(t *testing.T) { 2063 for _, testCase := range appendMatchingPropertiesTestCases() { 2064 t.Run(testCase.name, func(t *testing.T) { 2065 2066 got := testCase.dst 2067 var err error 2068 var testType string 2069 2070 order := func(dstField, srcField reflect.StructField) (Order, error) { 2071 switch testCase.order { 2072 case Append: 2073 return Append, nil 2074 case Prepend: 2075 return Prepend, nil 2076 case Replace: 2077 return Replace, nil 2078 } 2079 return Append, errors.New("unknown order") 2080 } 2081 2082 switch testCase.order { 2083 case Append: 2084 testType = "prepend matching" 2085 case Prepend: 2086 testType = "append matching" 2087 case Replace: 2088 testType = "replace matching" 2089 } 2090 2091 err = ExtendMatchingProperties(got, testCase.src, testCase.filter, order) 2092 2093 check(t, testType, testCase.name, got, err, testCase.out, testCase.err) 2094 }) 2095 } 2096} 2097 2098func check(t *testing.T, testType, testString string, 2099 got interface{}, err error, 2100 expected interface{}, expectedErr error) { 2101 2102 printedTestCase := false 2103 e := func(s string, expected, got interface{}) { 2104 if !printedTestCase { 2105 t.Errorf("test case %s: %s", testType, testString) 2106 printedTestCase = true 2107 } 2108 t.Errorf("incorrect %s", s) 2109 t.Errorf(" expected: %s", p(expected)) 2110 t.Errorf(" got: %s", p(got)) 2111 } 2112 2113 if err != nil { 2114 if expectedErr != nil { 2115 if err.Error() != expectedErr.Error() { 2116 e("unexpected error", expectedErr.Error(), err.Error()) 2117 } 2118 } else { 2119 e("unexpected error", nil, err.Error()) 2120 } 2121 } else { 2122 if expectedErr != nil { 2123 e("missing error", expectedErr, nil) 2124 } 2125 } 2126 2127 if !reflect.DeepEqual(expected, got) { 2128 e("output:", expected, got) 2129 } 2130} 2131 2132func p(in interface{}) string { 2133 if v, ok := in.([]interface{}); ok { 2134 s := make([]string, len(v)) 2135 for i := range v { 2136 s[i] = fmt.Sprintf("%#v", v[i]) 2137 } 2138 return "[" + strings.Join(s, ", ") + "]" 2139 } else { 2140 return fmt.Sprintf("%#v", in) 2141 } 2142} 2143