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
306func Test_PropertiesToApply(t *testing.T) {
307	mt, _ := newModuleType(&ModuleTypeProperties{
308		Module_type:      "foo",
309		Config_namespace: "bar",
310		Bool_variables:   []string{"bool_var"},
311		Properties:       []string{"a", "b"},
312	})
313	boolVarPositive := &properties{
314		A: proptools.StringPtr("A"),
315		B: true,
316	}
317	conditionsDefault := &properties{
318		A: proptools.StringPtr("default"),
319		B: false,
320	}
321	actualProps := &struct {
322		Soong_config_variables soongConfigVars
323	}{
324		Soong_config_variables: soongConfigVars{
325			Bool_var: &boolVarProps{
326				A:                  boolVarPositive.A,
327				B:                  boolVarPositive.B,
328				Conditions_default: conditionsDefault,
329			},
330		},
331	}
332	props := reflect.ValueOf(actualProps)
333
334	testCases := []struct {
335		name      string
336		config    SoongConfig
337		wantProps []interface{}
338	}{
339		{
340			name:      "no_vendor_config",
341			config:    Config(map[string]string{}),
342			wantProps: []interface{}{conditionsDefault},
343		},
344		{
345			name:      "vendor_config_false",
346			config:    Config(map[string]string{"bool_var": "n"}),
347			wantProps: []interface{}{conditionsDefault},
348		},
349		{
350			name:      "bool_var_true",
351			config:    Config(map[string]string{"bool_var": "y"}),
352			wantProps: []interface{}{boolVarPositive},
353		},
354	}
355
356	for _, tc := range testCases {
357		gotProps, err := PropertiesToApply(mt, props, tc.config)
358		if err != nil {
359			t.Errorf("%s: Unexpected error in PropertiesToApply: %s", tc.name, err)
360		}
361
362		if !reflect.DeepEqual(gotProps, tc.wantProps) {
363			t.Errorf("%s: Expected %s, got %s", tc.name, tc.wantProps, gotProps)
364		}
365	}
366}
367