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