• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2020 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 soongconfig
16
17import (
18	"reflect"
19	"testing"
20
21	"github.com/google/blueprint/proptools"
22)
23
24func Test_CanonicalizeToProperty(t *testing.T) {
25	tests := []struct {
26		name string
27		arg  string
28		want string
29	}{
30		{
31			name: "lowercase",
32			arg:  "board",
33			want: "board",
34		},
35		{
36			name: "uppercase",
37			arg:  "BOARD",
38			want: "BOARD",
39		},
40		{
41			name: "numbers",
42			arg:  "BOARD123",
43			want: "BOARD123",
44		},
45		{
46			name: "underscore",
47			arg:  "TARGET_BOARD",
48			want: "TARGET_BOARD",
49		},
50		{
51			name: "dash",
52			arg:  "TARGET-BOARD",
53			want: "TARGET_BOARD",
54		},
55		{
56			name: "unicode",
57			arg:  "boardλ",
58			want: "board_",
59		},
60	}
61	for _, tt := range tests {
62		t.Run(tt.name, func(t *testing.T) {
63			if got := CanonicalizeToProperty(tt.arg); got != tt.want {
64				t.Errorf("canonicalizeToProperty() = %v, want %v", got, tt.want)
65			}
66		})
67	}
68}
69
70func Test_typeForPropertyFromPropertyStruct(t *testing.T) {
71	tests := []struct {
72		name     string
73		ps       interface{}
74		property string
75		want     string
76	}{
77		{
78			name: "string",
79			ps: struct {
80				A string
81			}{},
82			property: "a",
83			want:     "string",
84		},
85		{
86			name: "list",
87			ps: struct {
88				A []string
89			}{},
90			property: "a",
91			want:     "[]string",
92		},
93		{
94			name: "missing",
95			ps: struct {
96				A []string
97			}{},
98			property: "b",
99			want:     "",
100		},
101		{
102			name: "nested",
103			ps: struct {
104				A struct {
105					B string
106				}
107			}{},
108			property: "a.b",
109			want:     "string",
110		},
111		{
112			name: "missing nested",
113			ps: struct {
114				A struct {
115					B string
116				}
117			}{},
118			property: "a.c",
119			want:     "",
120		},
121		{
122			name: "not a struct",
123			ps: struct {
124				A string
125			}{},
126			property: "a.b",
127			want:     "",
128		},
129		{
130			name: "nested pointer",
131			ps: struct {
132				A *struct {
133					B string
134				}
135			}{},
136			property: "a.b",
137			want:     "string",
138		},
139		{
140			name: "nested interface",
141			ps: struct {
142				A interface{}
143			}{
144				A: struct {
145					B string
146				}{},
147			},
148			property: "a.b",
149			want:     "string",
150		},
151		{
152			name: "nested interface pointer",
153			ps: struct {
154				A interface{}
155			}{
156				A: &struct {
157					B string
158				}{},
159			},
160			property: "a.b",
161			want:     "string",
162		},
163		{
164			name: "nested interface nil pointer",
165			ps: struct {
166				A interface{}
167			}{
168				A: (*struct {
169					B string
170				})(nil),
171			},
172			property: "a.b",
173			want:     "string",
174		},
175	}
176	for _, tt := range tests {
177		t.Run(tt.name, func(t *testing.T) {
178			typ := typeForPropertyFromPropertyStruct(tt.ps, tt.property)
179			got := ""
180			if typ != nil {
181				got = typ.String()
182			}
183			if got != tt.want {
184				t.Errorf("typeForPropertyFromPropertyStruct() = %v, want %v", got, tt.want)
185			}
186		})
187	}
188}
189
190func Test_createAffectablePropertiesType(t *testing.T) {
191	tests := []struct {
192		name                 string
193		affectableProperties []string
194		factoryProps         interface{}
195		want                 string
196	}{
197		{
198			name:                 "string",
199			affectableProperties: []string{"cflags"},
200			factoryProps: struct {
201				Cflags string
202			}{},
203			want: "*struct { Cflags string }",
204		},
205		{
206			name:                 "list",
207			affectableProperties: []string{"cflags"},
208			factoryProps: struct {
209				Cflags []string
210			}{},
211			want: "*struct { Cflags []string }",
212		},
213		{
214			name:                 "string pointer",
215			affectableProperties: []string{"cflags"},
216			factoryProps: struct {
217				Cflags *string
218			}{},
219			want: "*struct { Cflags *string }",
220		},
221		{
222			name:                 "subset",
223			affectableProperties: []string{"cflags"},
224			factoryProps: struct {
225				Cflags  string
226				Ldflags string
227			}{},
228			want: "*struct { Cflags string }",
229		},
230		{
231			name:                 "none",
232			affectableProperties: []string{"cflags"},
233			factoryProps: struct {
234				Ldflags string
235			}{},
236			want: "",
237		},
238		{
239			name:                 "nested",
240			affectableProperties: []string{"multilib.lib32.cflags"},
241			factoryProps: struct {
242				Multilib struct {
243					Lib32 struct {
244						Cflags string
245					}
246				}
247			}{},
248			want: "*struct { Multilib struct { Lib32 struct { Cflags string } } }",
249		},
250		{
251			name: "complex",
252			affectableProperties: []string{
253				"cflags",
254				"multilib.lib32.cflags",
255				"multilib.lib32.ldflags",
256				"multilib.lib64.cflags",
257				"multilib.lib64.ldflags",
258				"zflags",
259			},
260			factoryProps: struct {
261				Cflags   string
262				Multilib struct {
263					Lib32 struct {
264						Cflags  string
265						Ldflags string
266					}
267					Lib64 struct {
268						Cflags  string
269						Ldflags string
270					}
271				}
272				Zflags string
273			}{},
274			want: "*struct { Cflags string; Multilib struct { Lib32 struct { Cflags string; Ldflags string }; Lib64 struct { Cflags string; Ldflags string } }; Zflags string }",
275		},
276	}
277	for _, tt := range tests {
278		t.Run(tt.name, func(t *testing.T) {
279			typ := createAffectablePropertiesType(tt.affectableProperties, []interface{}{tt.factoryProps})
280			got := ""
281			if typ != nil {
282				got = typ.String()
283			}
284			if !reflect.DeepEqual(got, tt.want) {
285				t.Errorf("createAffectablePropertiesType() = %v, want %v", got, tt.want)
286			}
287		})
288	}
289}
290
291type properties struct {
292	A *string
293	B bool
294}
295
296type boolVarProps struct {
297	A                  *string
298	B                  bool
299	Conditions_default *properties
300}
301
302type soongConfigVars struct {
303	Bool_var interface{}
304}
305
306type stringSoongConfigVars struct {
307	String_var interface{}
308}
309
310func Test_PropertiesToApply(t *testing.T) {
311	mt, _ := newModuleType(&ModuleTypeProperties{
312		Module_type:      "foo",
313		Config_namespace: "bar",
314		Bool_variables:   []string{"bool_var"},
315		Properties:       []string{"a", "b"},
316	})
317	boolVarPositive := &properties{
318		A: proptools.StringPtr("A"),
319		B: true,
320	}
321	conditionsDefault := &properties{
322		A: proptools.StringPtr("default"),
323		B: false,
324	}
325	actualProps := &struct {
326		Soong_config_variables soongConfigVars
327	}{
328		Soong_config_variables: soongConfigVars{
329			Bool_var: &boolVarProps{
330				A:                  boolVarPositive.A,
331				B:                  boolVarPositive.B,
332				Conditions_default: conditionsDefault,
333			},
334		},
335	}
336	props := reflect.ValueOf(actualProps)
337
338	testCases := []struct {
339		name      string
340		config    SoongConfig
341		wantProps []interface{}
342	}{
343		{
344			name:      "no_vendor_config",
345			config:    Config(map[string]string{}),
346			wantProps: []interface{}{conditionsDefault},
347		},
348		{
349			name:      "vendor_config_false",
350			config:    Config(map[string]string{"bool_var": "n"}),
351			wantProps: []interface{}{conditionsDefault},
352		},
353		{
354			name:      "bool_var_true",
355			config:    Config(map[string]string{"bool_var": "y"}),
356			wantProps: []interface{}{boolVarPositive},
357		},
358	}
359
360	for _, tc := range testCases {
361		gotProps, err := PropertiesToApply(mt, props, tc.config)
362		if err != nil {
363			t.Errorf("%s: Unexpected error in PropertiesToApply: %s", tc.name, err)
364		}
365
366		if !reflect.DeepEqual(gotProps, tc.wantProps) {
367			t.Errorf("%s: Expected %s, got %s", tc.name, tc.wantProps, gotProps)
368		}
369	}
370}
371
372func Test_PropertiesToApply_String_Error(t *testing.T) {
373	mt, _ := newModuleType(&ModuleTypeProperties{
374		Module_type:      "foo",
375		Config_namespace: "bar",
376		Variables:        []string{"string_var"},
377		Properties:       []string{"a", "b"},
378	})
379	mt.Variables = append(mt.Variables, &stringVariable{
380		baseVariable: baseVariable{
381			variable: "string_var",
382		},
383		values: []string{"a", "b", "c"},
384	})
385	stringVarPositive := &properties{
386		A: proptools.StringPtr("A"),
387		B: true,
388	}
389	conditionsDefault := &properties{
390		A: proptools.StringPtr("default"),
391		B: false,
392	}
393	actualProps := &struct {
394		Soong_config_variables stringSoongConfigVars
395	}{
396		Soong_config_variables: stringSoongConfigVars{
397			String_var: &boolVarProps{
398				A:                  stringVarPositive.A,
399				B:                  stringVarPositive.B,
400				Conditions_default: conditionsDefault,
401			},
402		},
403	}
404	props := reflect.ValueOf(actualProps)
405
406	_, err := PropertiesToApply(mt, props, Config(map[string]string{
407		"string_var": "x",
408	}))
409	expected := `Soong config property "string_var" must be one of [a b c], found "x"`
410	if err == nil {
411		t.Fatalf("Expected an error, got nil")
412	} else if err.Error() != expected {
413		t.Fatalf("Error message was not correct, expected %q, got %q", expected, err.Error())
414	}
415}
416
417func Test_Bp2BuildSoongConfigDefinitionsAddVars(t *testing.T) {
418	testCases := []struct {
419		desc     string
420		defs     []*SoongConfigDefinition
421		expected Bp2BuildSoongConfigDefinitions
422	}{
423		{
424			desc: "non-overlapping",
425			defs: []*SoongConfigDefinition{
426				&SoongConfigDefinition{
427					ModuleTypes: map[string]*ModuleType{
428						"a": &ModuleType{
429							ConfigNamespace: "foo",
430							Variables: []soongConfigVariable{
431								&stringVariable{
432									baseVariable: baseVariable{"string_var"},
433									values:       []string{"a", "b", "c"},
434								},
435							},
436						},
437					},
438				},
439				&SoongConfigDefinition{
440					ModuleTypes: map[string]*ModuleType{
441						"b": &ModuleType{
442							ConfigNamespace: "foo",
443							Variables: []soongConfigVariable{
444								&stringVariable{
445									baseVariable: baseVariable{"string_var"},
446									values:       []string{"a", "b", "c"},
447								},
448								&boolVariable{baseVariable: baseVariable{"bool_var"}},
449								&valueVariable{baseVariable: baseVariable{"variable_var"}},
450							},
451						},
452					},
453				},
454			},
455			expected: Bp2BuildSoongConfigDefinitions{
456				StringVars: map[string]map[string]bool{
457					"foo__string_var": map[string]bool{"a": true, "b": true, "c": true},
458				},
459				BoolVars:  map[string]bool{"foo__bool_var": true},
460				ValueVars: map[string]bool{"foo__variable_var": true},
461			},
462		},
463		{
464			desc: "overlapping",
465			defs: []*SoongConfigDefinition{
466				&SoongConfigDefinition{
467					ModuleTypes: map[string]*ModuleType{
468						"a": &ModuleType{
469							ConfigNamespace: "foo",
470							Variables: []soongConfigVariable{
471								&stringVariable{
472									baseVariable: baseVariable{"string_var"},
473									values:       []string{"a", "b", "c"},
474								},
475							},
476						},
477					},
478				},
479				&SoongConfigDefinition{
480					ModuleTypes: map[string]*ModuleType{
481						"b": &ModuleType{
482							ConfigNamespace: "foo",
483							Variables: []soongConfigVariable{
484								&stringVariable{
485									baseVariable: baseVariable{"string_var"},
486									values:       []string{"b", "c", "d"},
487								},
488								&boolVariable{baseVariable: baseVariable{"bool_var"}},
489								&valueVariable{baseVariable: baseVariable{"variable_var"}},
490							},
491						},
492					},
493				},
494			},
495			expected: Bp2BuildSoongConfigDefinitions{
496				StringVars: map[string]map[string]bool{
497					"foo__string_var": map[string]bool{"a": true, "b": true, "c": true, "d": true},
498				},
499				BoolVars:  map[string]bool{"foo__bool_var": true},
500				ValueVars: map[string]bool{"foo__variable_var": true},
501			},
502		},
503	}
504
505	for _, tc := range testCases {
506		t.Run(tc.desc, func(t *testing.T) {
507			actual := &Bp2BuildSoongConfigDefinitions{}
508			for _, d := range tc.defs {
509				func(def *SoongConfigDefinition) {
510					actual.AddVars(def)
511				}(d)
512			}
513			if !reflect.DeepEqual(*actual, tc.expected) {
514				t.Errorf("Expected %#v, got %#v", tc.expected, *actual)
515			}
516		})
517	}
518
519}
520
521func Test_Bp2BuildSoongConfigDefinitions(t *testing.T) {
522	testCases := []struct {
523		desc     string
524		defs     Bp2BuildSoongConfigDefinitions
525		expected string
526	}{
527		{
528			desc: "all empty",
529			defs: Bp2BuildSoongConfigDefinitions{},
530			expected: `soong_config_bool_variables = {}
531
532soong_config_value_variables = {}
533
534soong_config_string_variables = {}`}, {
535			desc: "only bool",
536			defs: Bp2BuildSoongConfigDefinitions{
537				BoolVars: map[string]bool{
538					"bool_var": true,
539				},
540			},
541			expected: `soong_config_bool_variables = {
542    "bool_var": True,
543}
544
545soong_config_value_variables = {}
546
547soong_config_string_variables = {}`}, {
548			desc: "only value vars",
549			defs: Bp2BuildSoongConfigDefinitions{
550				ValueVars: map[string]bool{
551					"value_var": true,
552				},
553			},
554			expected: `soong_config_bool_variables = {}
555
556soong_config_value_variables = {
557    "value_var": True,
558}
559
560soong_config_string_variables = {}`}, {
561			desc: "only string vars",
562			defs: Bp2BuildSoongConfigDefinitions{
563				StringVars: map[string]map[string]bool{
564					"string_var": map[string]bool{
565						"choice1": true,
566						"choice2": true,
567						"choice3": true,
568					},
569				},
570			},
571			expected: `soong_config_bool_variables = {}
572
573soong_config_value_variables = {}
574
575soong_config_string_variables = {
576    "string_var": [
577        "choice1",
578        "choice2",
579        "choice3",
580    ],
581}`}, {
582			desc: "all vars",
583			defs: Bp2BuildSoongConfigDefinitions{
584				BoolVars: map[string]bool{
585					"bool_var_one": true,
586				},
587				ValueVars: map[string]bool{
588					"value_var_one": true,
589					"value_var_two": true,
590				},
591				StringVars: map[string]map[string]bool{
592					"string_var_one": map[string]bool{
593						"choice1": true,
594						"choice2": true,
595						"choice3": true,
596					},
597					"string_var_two": map[string]bool{
598						"foo": true,
599						"bar": true,
600					},
601				},
602			},
603			expected: `soong_config_bool_variables = {
604    "bool_var_one": True,
605}
606
607soong_config_value_variables = {
608    "value_var_one": True,
609    "value_var_two": True,
610}
611
612soong_config_string_variables = {
613    "string_var_one": [
614        "choice1",
615        "choice2",
616        "choice3",
617    ],
618    "string_var_two": [
619        "bar",
620        "foo",
621    ],
622}`},
623	}
624	for _, test := range testCases {
625		t.Run(test.desc, func(t *testing.T) {
626			actual := test.defs.String()
627			if actual != test.expected {
628				t.Errorf("Expected:\n%s\nbut got:\n%s", test.expected, actual)
629			}
630		})
631	}
632}
633