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