1// Copyright 2014 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 "bytes" 19 "reflect" 20 21 "testing" 22 23 "github.com/google/blueprint/parser" 24) 25 26var validUnpackTestCases = []struct { 27 name string 28 input string 29 output []interface{} 30 empty []interface{} 31 errs []error 32}{ 33 { 34 name: "blank and unset", 35 input: ` 36 m { 37 s: "abc", 38 blank: "", 39 } 40 `, 41 output: []interface{}{ 42 &struct { 43 S *string 44 Blank *string 45 Unset *string 46 }{ 47 S: StringPtr("abc"), 48 Blank: StringPtr(""), 49 Unset: nil, 50 }, 51 }, 52 }, 53 54 { 55 name: "string", 56 input: ` 57 m { 58 s: "abc", 59 } 60 `, 61 output: []interface{}{ 62 &struct { 63 S string 64 }{ 65 S: "abc", 66 }, 67 }, 68 }, 69 70 { 71 name: "bool", 72 input: ` 73 m { 74 isGood: true, 75 } 76 `, 77 output: []interface{}{ 78 &struct { 79 IsGood bool 80 }{ 81 IsGood: true, 82 }, 83 }, 84 }, 85 86 { 87 name: "boolptr", 88 input: ` 89 m { 90 isGood: true, 91 isBad: false, 92 } 93 `, 94 output: []interface{}{ 95 &struct { 96 IsGood *bool 97 IsBad *bool 98 IsUgly *bool 99 }{ 100 IsGood: BoolPtr(true), 101 IsBad: BoolPtr(false), 102 IsUgly: nil, 103 }, 104 }, 105 }, 106 107 { 108 name: "slice", 109 input: ` 110 m { 111 stuff: ["asdf", "jkl;", "qwert", 112 "uiop", "bnm,"], 113 empty: [] 114 } 115 `, 116 output: []interface{}{ 117 &struct { 118 Stuff []string 119 Empty []string 120 Nil []string 121 NonString []struct{ S string } `blueprint:"mutated"` 122 }{ 123 Stuff: []string{"asdf", "jkl;", "qwert", "uiop", "bnm,"}, 124 Empty: []string{}, 125 Nil: nil, 126 NonString: nil, 127 }, 128 }, 129 }, 130 131 { 132 name: "map", 133 input: ` 134 m { 135 stuff: { "asdf": "jkl;", "qwert": "uiop"}, 136 empty: {}, 137 nested: { 138 other_stuff: {}, 139 }, 140 } 141 `, 142 output: []interface{}{ 143 &struct { 144 Stuff map[string]string 145 Empty map[string]string 146 Nil map[string]string 147 NonString map[string]struct{ S string } `blueprint:"mutated"` 148 Nested struct { 149 Other_stuff map[string]string 150 } 151 }{ 152 Stuff: map[string]string{"asdf": "jkl;", "qwert": "uiop"}, 153 Empty: map[string]string{}, 154 Nil: nil, 155 NonString: nil, 156 Nested: struct{ Other_stuff map[string]string }{ 157 Other_stuff: map[string]string{}, 158 }, 159 }, 160 }, 161 }, 162 163 { 164 name: "map with slice", 165 input: ` 166 m { 167 stuff: { "asdf": ["jkl;"], "qwert": []}, 168 empty: {}, 169 } 170 `, 171 output: []interface{}{ 172 &struct { 173 Stuff map[string][]string 174 Empty map[string][]string 175 Nil map[string][]string 176 NonString map[string]struct{ S string } `blueprint:"mutated"` 177 }{ 178 Stuff: map[string][]string{"asdf": []string{"jkl;"}, "qwert": []string{}}, 179 Empty: map[string][]string{}, 180 Nil: nil, 181 NonString: nil, 182 }, 183 }, 184 }, 185 186 { 187 name: "map with struct", 188 input: ` 189 m { 190 stuff: { "asdf": {s:"a"}}, 191 empty: {}, 192 } 193 `, 194 output: []interface{}{ 195 &struct { 196 Stuff map[string]struct{ S string } 197 Empty map[string]struct{ S string } 198 Nil map[string]struct{ S string } 199 }{ 200 Stuff: map[string]struct{ S string }{"asdf": struct{ S string }{"a"}}, 201 Empty: map[string]struct{ S string }{}, 202 Nil: nil, 203 }, 204 }, 205 }, 206 207 { 208 name: "double nested", 209 input: ` 210 m { 211 nested: { 212 nested: { 213 s: "abc", 214 }, 215 }, 216 } 217 `, 218 output: []interface{}{ 219 &struct { 220 Nested struct { 221 Nested struct { 222 S string 223 } 224 } 225 }{ 226 Nested: struct{ Nested struct{ S string } }{ 227 Nested: struct{ S string }{ 228 S: "abc", 229 }, 230 }, 231 }, 232 }, 233 }, 234 235 { 236 name: "nested", 237 input: ` 238 m { 239 nested: { 240 s: "abc", 241 } 242 } 243 `, 244 output: []interface{}{ 245 &struct { 246 Nested struct { 247 S string 248 } 249 }{ 250 Nested: struct{ S string }{ 251 S: "abc", 252 }, 253 }, 254 }, 255 }, 256 257 { 258 name: "nested interface", 259 input: ` 260 m { 261 nested: { 262 s: "def", 263 } 264 } 265 `, 266 output: []interface{}{ 267 &struct { 268 Nested interface{} 269 }{ 270 Nested: &struct{ S string }{ 271 S: "def", 272 }, 273 }, 274 }, 275 }, 276 277 { 278 name: "mixed", 279 input: ` 280 m { 281 nested: { 282 foo: "abc", 283 }, 284 bar: false, 285 baz: ["def", "ghi"], 286 } 287 `, 288 output: []interface{}{ 289 &struct { 290 Nested struct { 291 Foo string 292 } 293 Bar bool 294 Baz []string 295 }{ 296 Nested: struct{ Foo string }{ 297 Foo: "abc", 298 }, 299 Bar: false, 300 Baz: []string{"def", "ghi"}, 301 }, 302 }, 303 }, 304 305 { 306 name: "filter", 307 input: ` 308 m { 309 nested: { 310 foo: "abc", 311 }, 312 bar: false, 313 baz: ["def", "ghi"], 314 } 315 `, 316 output: []interface{}{ 317 &struct { 318 Nested struct { 319 Foo string `allowNested:"true"` 320 } `blueprint:"filter(allowNested:\"true\")"` 321 Bar bool 322 Baz []string 323 }{ 324 Nested: struct { 325 Foo string `allowNested:"true"` 326 }{ 327 Foo: "abc", 328 }, 329 Bar: false, 330 Baz: []string{"def", "ghi"}, 331 }, 332 }, 333 }, 334 335 // List of maps 336 { 337 name: "list of structs", 338 input: ` 339 m { 340 mapslist: [ 341 { 342 foo: "abc", 343 bar: true, 344 }, 345 { 346 foo: "def", 347 bar: false, 348 } 349 ], 350 } 351 `, 352 output: []interface{}{ 353 &struct { 354 Mapslist []struct { 355 Foo string 356 Bar bool 357 } 358 }{ 359 Mapslist: []struct { 360 Foo string 361 Bar bool 362 }{ 363 {Foo: "abc", Bar: true}, 364 {Foo: "def", Bar: false}, 365 }, 366 }, 367 }, 368 }, 369 370 // List of pointers to structs 371 { 372 name: "list of pointers to structs", 373 input: ` 374 m { 375 mapslist: [ 376 { 377 foo: "abc", 378 bar: true, 379 }, 380 { 381 foo: "def", 382 bar: false, 383 } 384 ], 385 } 386 `, 387 output: []interface{}{ 388 &struct { 389 Mapslist []*struct { 390 Foo string 391 Bar bool 392 } 393 }{ 394 Mapslist: []*struct { 395 Foo string 396 Bar bool 397 }{ 398 {Foo: "abc", Bar: true}, 399 {Foo: "def", Bar: false}, 400 }, 401 }, 402 }, 403 }, 404 405 // List of lists 406 { 407 name: "list of lists", 408 input: ` 409 m { 410 listoflists: [ 411 ["abc",], 412 ["def",], 413 ], 414 } 415 `, 416 output: []interface{}{ 417 &struct { 418 Listoflists [][]string 419 }{ 420 Listoflists: [][]string{ 421 []string{"abc"}, 422 []string{"def"}, 423 }, 424 }, 425 }, 426 }, 427 428 // Multilevel 429 { 430 name: "multilevel", 431 input: ` 432 m { 433 name: "mymodule", 434 flag: true, 435 settings: ["foo1", "foo2", "foo3",], 436 perarch: { 437 arm: "32", 438 arm64: "64", 439 }, 440 configvars: [ 441 { var: "var1", values: ["1.1", "1.2", ], }, 442 { var: "var2", values: ["2.1", ], }, 443 ], 444 } 445 `, 446 output: []interface{}{ 447 &struct { 448 Name string 449 Flag bool 450 Settings []string 451 Perarch *struct { 452 Arm string 453 Arm64 string 454 } 455 Configvars []struct { 456 Var string 457 Values []string 458 } 459 }{ 460 Name: "mymodule", 461 Flag: true, 462 Settings: []string{"foo1", "foo2", "foo3"}, 463 Perarch: &struct { 464 Arm string 465 Arm64 string 466 }{Arm: "32", Arm64: "64"}, 467 Configvars: []struct { 468 Var string 469 Values []string 470 }{ 471 {Var: "var1", Values: []string{"1.1", "1.2"}}, 472 {Var: "var2", Values: []string{"2.1"}}, 473 }, 474 }, 475 }, 476 }, 477 // Anonymous struct 478 { 479 name: "embedded struct", 480 input: ` 481 m { 482 s: "abc", 483 nested: { 484 s: "def", 485 }, 486 } 487 `, 488 output: []interface{}{ 489 &struct { 490 EmbeddedStruct 491 Nested struct { 492 EmbeddedStruct 493 } 494 }{ 495 EmbeddedStruct: EmbeddedStruct{ 496 S: "abc", 497 }, 498 Nested: struct { 499 EmbeddedStruct 500 }{ 501 EmbeddedStruct: EmbeddedStruct{ 502 S: "def", 503 }, 504 }, 505 }, 506 }, 507 }, 508 509 // Anonymous interface 510 { 511 name: "embedded interface", 512 input: ` 513 m { 514 s: "abc", 515 nested: { 516 s: "def", 517 }, 518 } 519 `, 520 output: []interface{}{ 521 &struct { 522 EmbeddedInterface 523 Nested struct { 524 EmbeddedInterface 525 } 526 }{ 527 EmbeddedInterface: &struct{ S string }{ 528 S: "abc", 529 }, 530 Nested: struct { 531 EmbeddedInterface 532 }{ 533 EmbeddedInterface: &struct{ S string }{ 534 S: "def", 535 }, 536 }, 537 }, 538 }, 539 }, 540 541 // Anonymous struct with name collision 542 { 543 name: "embedded name collision", 544 input: ` 545 m { 546 s: "abc", 547 nested: { 548 s: "def", 549 }, 550 } 551 `, 552 output: []interface{}{ 553 &struct { 554 S string 555 EmbeddedStruct 556 Nested struct { 557 S string 558 EmbeddedStruct 559 } 560 }{ 561 S: "abc", 562 EmbeddedStruct: EmbeddedStruct{ 563 S: "abc", 564 }, 565 Nested: struct { 566 S string 567 EmbeddedStruct 568 }{ 569 S: "def", 570 EmbeddedStruct: EmbeddedStruct{ 571 S: "def", 572 }, 573 }, 574 }, 575 }, 576 }, 577 578 // Anonymous interface with name collision 579 { 580 name: "embeded interface name collision", 581 input: ` 582 m { 583 s: "abc", 584 nested: { 585 s: "def", 586 }, 587 } 588 `, 589 output: []interface{}{ 590 &struct { 591 S string 592 EmbeddedInterface 593 Nested struct { 594 S string 595 EmbeddedInterface 596 } 597 }{ 598 S: "abc", 599 EmbeddedInterface: &struct{ S string }{ 600 S: "abc", 601 }, 602 Nested: struct { 603 S string 604 EmbeddedInterface 605 }{ 606 S: "def", 607 EmbeddedInterface: &struct{ S string }{ 608 S: "def", 609 }, 610 }, 611 }, 612 }, 613 }, 614 615 // Variables 616 { 617 name: "variables", 618 input: ` 619 list = ["abc"] 620 string = "def" 621 list_with_variable = [string] 622 struct_value = { name: "foo" } 623 m { 624 s: string, 625 list: list, 626 list2: list_with_variable, 627 structattr: struct_value, 628 } 629 `, 630 output: []interface{}{ 631 &struct { 632 S string 633 List []string 634 List2 []string 635 Structattr struct { 636 Name string 637 } 638 }{ 639 S: "def", 640 List: []string{"abc"}, 641 List2: []string{"def"}, 642 Structattr: struct { 643 Name string 644 }{ 645 Name: "foo", 646 }, 647 }, 648 }, 649 }, 650 651 // Multiple property structs 652 { 653 name: "multiple", 654 input: ` 655 m { 656 nested: { 657 s: "abc", 658 } 659 } 660 `, 661 output: []interface{}{ 662 &struct { 663 Nested struct { 664 S string 665 } 666 }{ 667 Nested: struct{ S string }{ 668 S: "abc", 669 }, 670 }, 671 &struct { 672 Nested struct { 673 S string 674 } 675 }{ 676 Nested: struct{ S string }{ 677 S: "abc", 678 }, 679 }, 680 &struct { 681 }{}, 682 }, 683 }, 684 685 // Nil pointer to struct 686 { 687 name: "nil struct pointer", 688 input: ` 689 m { 690 nested: { 691 s: "abc", 692 } 693 } 694 `, 695 output: []interface{}{ 696 &struct { 697 Nested *struct { 698 S string 699 } 700 }{ 701 Nested: &struct{ S string }{ 702 S: "abc", 703 }, 704 }, 705 }, 706 empty: []interface{}{ 707 &struct { 708 Nested *struct { 709 S string 710 } 711 }{}, 712 }, 713 }, 714 715 // Interface containing nil pointer to struct 716 { 717 name: "interface nil struct pointer", 718 input: ` 719 m { 720 nested: { 721 s: "abc", 722 } 723 } 724 `, 725 output: []interface{}{ 726 &struct { 727 Nested interface{} 728 }{ 729 Nested: &EmbeddedStruct{ 730 S: "abc", 731 }, 732 }, 733 }, 734 empty: []interface{}{ 735 &struct { 736 Nested interface{} 737 }{ 738 Nested: (*EmbeddedStruct)(nil), 739 }, 740 }, 741 }, 742 743 // Factory set properties 744 { 745 name: "factory properties", 746 input: ` 747 m { 748 string: "abc", 749 string_ptr: "abc", 750 bool: false, 751 bool_ptr: false, 752 list: ["a", "b", "c"], 753 } 754 `, 755 output: []interface{}{ 756 &struct { 757 String string 758 String_ptr *string 759 Bool bool 760 Bool_ptr *bool 761 List []string 762 }{ 763 String: "012abc", 764 String_ptr: StringPtr("abc"), 765 Bool: true, 766 Bool_ptr: BoolPtr(false), 767 List: []string{"0", "1", "2", "a", "b", "c"}, 768 }, 769 }, 770 empty: []interface{}{ 771 &struct { 772 String string 773 String_ptr *string 774 Bool bool 775 Bool_ptr *bool 776 List []string 777 }{ 778 String: "012", 779 String_ptr: StringPtr("012"), 780 Bool: true, 781 Bool_ptr: BoolPtr(true), 782 List: []string{"0", "1", "2"}, 783 }, 784 }, 785 }, 786 // Captitalized property 787 { 788 input: ` 789 m { 790 CAPITALIZED: "foo", 791 } 792 `, 793 output: []interface{}{ 794 &struct { 795 CAPITALIZED string 796 }{ 797 CAPITALIZED: "foo", 798 }, 799 }, 800 }, 801} 802 803func TestUnpackProperties(t *testing.T) { 804 for _, testCase := range validUnpackTestCases { 805 t.Run(testCase.name, func(t *testing.T) { 806 r := bytes.NewBufferString(testCase.input) 807 file, errs := parser.ParseAndEval("", r, parser.NewScope(nil)) 808 if len(errs) != 0 { 809 t.Errorf("test case: %s", testCase.input) 810 t.Errorf("unexpected parse errors:") 811 for _, err := range errs { 812 t.Errorf(" %s", err) 813 } 814 t.FailNow() 815 } 816 817 for _, def := range file.Defs { 818 module, ok := def.(*parser.Module) 819 if !ok { 820 continue 821 } 822 823 var output []interface{} 824 if len(testCase.empty) > 0 { 825 for _, p := range testCase.empty { 826 output = append(output, CloneProperties(reflect.ValueOf(p)).Interface()) 827 } 828 } else { 829 for _, p := range testCase.output { 830 output = append(output, CloneEmptyProperties(reflect.ValueOf(p)).Interface()) 831 } 832 } 833 834 _, errs = unpackProperties(module.Properties, []string{"stuff", "empty", "nil", "nested.other_stuff"}, output...) 835 if len(errs) != 0 && len(testCase.errs) == 0 { 836 t.Errorf("test case: %s", testCase.input) 837 t.Errorf("unexpected unpack errors:") 838 for _, err := range errs { 839 t.Errorf(" %s", err) 840 } 841 t.FailNow() 842 } else if !reflect.DeepEqual(errs, testCase.errs) { 843 t.Errorf("test case: %s", testCase.input) 844 t.Errorf("incorrect errors:") 845 t.Errorf(" expected: %+v", testCase.errs) 846 t.Errorf(" got: %+v", errs) 847 } 848 849 if len(output) != len(testCase.output) { 850 t.Fatalf("incorrect number of property structs, expected %d got %d", 851 len(testCase.output), len(output)) 852 } 853 854 for i := range output { 855 got := reflect.ValueOf(output[i]).Interface() 856 if !reflect.DeepEqual(got, testCase.output[i]) { 857 t.Errorf("test case: %s", testCase.input) 858 t.Errorf("incorrect output:") 859 t.Errorf(" expected: %+v", testCase.output[i]) 860 t.Errorf(" got: %+v", got) 861 } 862 } 863 } 864 }) 865 } 866} 867 868func TestUnpackErrors(t *testing.T) { 869 testCases := []struct { 870 name string 871 input string 872 output []interface{} 873 errors []string 874 }{ 875 { 876 name: "missing", 877 input: ` 878 m { 879 missing: true, 880 } 881 `, 882 output: []interface{}{}, 883 errors: []string{`<input>:3:13: unrecognized property "missing"`}, 884 }, 885 { 886 name: "missing nested", 887 input: ` 888 m { 889 nested: { 890 missing: true, 891 }, 892 } 893 `, 894 output: []interface{}{ 895 &struct { 896 Nested struct{} 897 }{}, 898 }, 899 errors: []string{`<input>:4:14: unrecognized property "nested.missing"`}, 900 }, 901 { 902 name: "mutated", 903 input: ` 904 m { 905 mutated: true, 906 } 907 `, 908 output: []interface{}{ 909 &struct { 910 Mutated bool `blueprint:"mutated"` 911 }{}, 912 }, 913 errors: []string{`<input>:3:13: mutated field mutated cannot be set in a Blueprint file`}, 914 }, 915 { 916 name: "nested mutated", 917 input: ` 918 m { 919 nested: { 920 mutated: true, 921 }, 922 } 923 `, 924 output: []interface{}{ 925 &struct { 926 Nested struct { 927 Mutated bool `blueprint:"mutated"` 928 } 929 }{}, 930 }, 931 errors: []string{`<input>:4:14: mutated field nested.mutated cannot be set in a Blueprint file`}, 932 }, 933 { 934 name: "duplicate", 935 input: ` 936 m { 937 exists: true, 938 exists: true, 939 } 940 `, 941 output: []interface{}{ 942 &struct { 943 Exists bool 944 }{}, 945 }, 946 errors: []string{ 947 `<input>:4:12: property "exists" already defined`, 948 `<input>:3:12: <-- previous definition here`, 949 }, 950 }, 951 { 952 name: "nested duplicate", 953 input: ` 954 m { 955 nested: { 956 exists: true, 957 exists: true, 958 }, 959 } 960 `, 961 output: []interface{}{ 962 &struct { 963 Nested struct { 964 Exists bool 965 } 966 }{}, 967 }, 968 errors: []string{ 969 `<input>:5:13: property "nested.exists" already defined`, 970 `<input>:4:13: <-- previous definition here`, 971 }, 972 }, 973 { 974 name: "wrong type", 975 input: ` 976 m { 977 int: "foo", 978 } 979 `, 980 output: []interface{}{ 981 &struct { 982 Int *int64 983 }{}, 984 }, 985 errors: []string{ 986 `<input>:3:11: can't assign string value to int64 property "int"`, 987 }, 988 }, 989 { 990 name: "wrong type for map", 991 input: ` 992 m { 993 map: "foo", 994 } 995 `, 996 output: []interface{}{ 997 &struct { 998 Map struct { 999 S string 1000 } 1001 }{}, 1002 }, 1003 errors: []string{ 1004 `<input>:3:11: can't assign string value to map property "map"`, 1005 }, 1006 }, 1007 { 1008 name: "wrong type for list", 1009 input: ` 1010 m { 1011 list: "foo", 1012 } 1013 `, 1014 output: []interface{}{ 1015 &struct { 1016 List []string 1017 }{}, 1018 }, 1019 errors: []string{ 1020 `<input>:3:12: can't assign string value to list property "list"`, 1021 }, 1022 }, 1023 { 1024 name: "wrong type for list of maps", 1025 input: ` 1026 m { 1027 map_list: "foo", 1028 } 1029 `, 1030 output: []interface{}{ 1031 &struct { 1032 Map_list []struct { 1033 S string 1034 } 1035 }{}, 1036 }, 1037 errors: []string{ 1038 `<input>:3:16: can't assign string value to list property "map_list"`, 1039 }, 1040 }, 1041 { 1042 name: "invalid use of maps", 1043 input: ` 1044 m { 1045 map: {"foo": "bar"}, 1046 } 1047 `, 1048 output: []interface{}{ 1049 &struct { 1050 Map map[string]string 1051 }{}, 1052 }, 1053 errors: []string{ 1054 `<input>: Uses of maps for properties must be allowlisted. "map" is an unsupported use case`, 1055 }, 1056 }, 1057 { 1058 name: "invalid use of maps, not used in bp file", 1059 input: ` 1060 m { 1061 } 1062 `, 1063 output: []interface{}{ 1064 &struct { 1065 Map map[string]string 1066 }{}, 1067 }, 1068 errors: []string{ 1069 `<input>: Uses of maps for properties must be allowlisted. "map" is an unsupported use case`, 1070 }, 1071 }, 1072 } 1073 1074 for _, testCase := range testCases { 1075 t.Run(testCase.name, func(t *testing.T) { 1076 r := bytes.NewBufferString(testCase.input) 1077 file, errs := parser.ParseAndEval("", r, parser.NewScope(nil)) 1078 if len(errs) != 0 { 1079 t.Errorf("test case: %s", testCase.input) 1080 t.Errorf("unexpected parse errors:") 1081 for _, err := range errs { 1082 t.Errorf(" %s", err) 1083 } 1084 t.FailNow() 1085 } 1086 1087 for _, def := range file.Defs { 1088 module, ok := def.(*parser.Module) 1089 if !ok { 1090 continue 1091 } 1092 1093 var output []interface{} 1094 for _, p := range testCase.output { 1095 output = append(output, CloneEmptyProperties(reflect.ValueOf(p)).Interface()) 1096 } 1097 1098 _, errs = UnpackProperties(module.Properties, output...) 1099 1100 printErrors := false 1101 for _, expectedErr := range testCase.errors { 1102 foundError := false 1103 for _, err := range errs { 1104 if err.Error() == expectedErr { 1105 foundError = true 1106 } 1107 } 1108 if !foundError { 1109 t.Errorf("expected error %s", expectedErr) 1110 printErrors = true 1111 } 1112 } 1113 if printErrors { 1114 t.Errorf("got errors:") 1115 for _, err := range errs { 1116 t.Errorf(" %s", err.Error()) 1117 } 1118 } 1119 } 1120 }) 1121 } 1122} 1123 1124func BenchmarkUnpackProperties(b *testing.B) { 1125 run := func(b *testing.B, props []interface{}, input string) { 1126 b.ReportAllocs() 1127 b.StopTimer() 1128 r := bytes.NewBufferString(input) 1129 file, errs := parser.ParseAndEval("", r, parser.NewScope(nil)) 1130 if len(errs) != 0 { 1131 b.Errorf("test case: %s", input) 1132 b.Errorf("unexpected parse errors:") 1133 for _, err := range errs { 1134 b.Errorf(" %s", err) 1135 } 1136 b.FailNow() 1137 } 1138 1139 for i := 0; i < b.N; i++ { 1140 for _, def := range file.Defs { 1141 module, ok := def.(*parser.Module) 1142 if !ok { 1143 continue 1144 } 1145 1146 var output []interface{} 1147 for _, p := range props { 1148 output = append(output, CloneProperties(reflect.ValueOf(p)).Interface()) 1149 } 1150 1151 b.StartTimer() 1152 _, errs = UnpackProperties(module.Properties, output...) 1153 b.StopTimer() 1154 if len(errs) > 0 { 1155 b.Errorf("unexpected unpack errors:") 1156 for _, err := range errs { 1157 b.Errorf(" %s", err) 1158 } 1159 } 1160 } 1161 } 1162 } 1163 1164 b.Run("basic", func(b *testing.B) { 1165 props := []interface{}{ 1166 &struct { 1167 Nested struct { 1168 S string 1169 } 1170 }{}, 1171 } 1172 bp := ` 1173 m { 1174 nested: { 1175 s: "abc", 1176 }, 1177 } 1178 ` 1179 run(b, props, bp) 1180 }) 1181 1182 b.Run("interface", func(b *testing.B) { 1183 props := []interface{}{ 1184 &struct { 1185 Nested interface{} 1186 }{ 1187 Nested: (*struct { 1188 S string 1189 })(nil), 1190 }, 1191 } 1192 bp := ` 1193 m { 1194 nested: { 1195 s: "abc", 1196 }, 1197 } 1198 ` 1199 run(b, props, bp) 1200 }) 1201 1202 b.Run("many", func(b *testing.B) { 1203 props := []interface{}{ 1204 &struct { 1205 A *string 1206 B *string 1207 C *string 1208 D *string 1209 E *string 1210 F *string 1211 G *string 1212 H *string 1213 I *string 1214 J *string 1215 }{}, 1216 } 1217 bp := ` 1218 m { 1219 a: "a", 1220 b: "b", 1221 c: "c", 1222 d: "d", 1223 e: "e", 1224 f: "f", 1225 g: "g", 1226 h: "h", 1227 i: "i", 1228 j: "j", 1229 } 1230 ` 1231 run(b, props, bp) 1232 }) 1233 1234 b.Run("deep", func(b *testing.B) { 1235 props := []interface{}{ 1236 &struct { 1237 Nested struct { 1238 Nested struct { 1239 Nested struct { 1240 Nested struct { 1241 Nested struct { 1242 Nested struct { 1243 Nested struct { 1244 Nested struct { 1245 Nested struct { 1246 Nested struct { 1247 S string 1248 } 1249 } 1250 } 1251 } 1252 } 1253 } 1254 } 1255 } 1256 } 1257 } 1258 }{}, 1259 } 1260 bp := ` 1261 m { 1262 nested: { nested: { nested: { nested: { nested: { 1263 nested: { nested: { nested: { nested: { nested: { 1264 s: "abc", 1265 }, }, }, }, }, 1266 }, }, }, }, }, 1267 } 1268 ` 1269 run(b, props, bp) 1270 }) 1271 1272 b.Run("mix", func(b *testing.B) { 1273 props := []interface{}{ 1274 &struct { 1275 Name string 1276 Flag bool 1277 Settings []string 1278 Perarch *struct { 1279 Arm string 1280 Arm64 string 1281 } 1282 Configvars []struct { 1283 Name string 1284 Values []string 1285 } 1286 }{}, 1287 } 1288 bp := ` 1289 m { 1290 name: "mymodule", 1291 flag: true, 1292 settings: ["foo1", "foo2", "foo3",], 1293 perarch: { 1294 arm: "32", 1295 arm64: "64", 1296 }, 1297 configvars: [ 1298 { name: "var1", values: ["var1:1", "var1:2", ], }, 1299 { name: "var2", values: ["var2:1", "var2:2", ], }, 1300 ], 1301 } 1302 ` 1303 run(b, props, bp) 1304 }) 1305} 1306