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 android
16
17import (
18	"reflect"
19	"strconv"
20	"testing"
21
22	"github.com/google/blueprint/proptools"
23)
24
25type printfIntoPropertyTestCase struct {
26	in  string
27	val interface{}
28	out string
29	err bool
30}
31
32var printfIntoPropertyTestCases = []printfIntoPropertyTestCase{
33	{
34		in:  "%d",
35		val: 0,
36		out: "0",
37	},
38	{
39		in:  "%d",
40		val: 1,
41		out: "1",
42	},
43	{
44		in:  "%d",
45		val: 2,
46		out: "2",
47	},
48	{
49		in:  "%d",
50		val: false,
51		out: "0",
52	},
53	{
54		in:  "%d",
55		val: true,
56		out: "1",
57	},
58	{
59		in:  "%d",
60		val: -1,
61		out: "-1",
62	},
63
64	{
65		in:  "-DA=%d",
66		val: 1,
67		out: "-DA=1",
68	},
69	{
70		in:  "-DA=%du",
71		val: 1,
72		out: "-DA=1u",
73	},
74	{
75		in:  "-DA=%s",
76		val: "abc",
77		out: "-DA=abc",
78	},
79	{
80		in:  `-DA="%s"`,
81		val: "abc",
82		out: `-DA="abc"`,
83	},
84
85	{
86		in:  "%%",
87		err: true,
88	},
89	{
90		in:  "%d%s",
91		err: true,
92	},
93	{
94		in:  "%d,%s",
95		err: true,
96	},
97	{
98		in:  "%d",
99		val: "",
100		err: true,
101	},
102	{
103		in:  "%d",
104		val: 1.5,
105		err: true,
106	},
107	{
108		in:  "%f",
109		val: 1.5,
110		err: true,
111	},
112}
113
114func TestPrintfIntoProperty(t *testing.T) {
115	for _, testCase := range printfIntoPropertyTestCases {
116		s := testCase.in
117		v := reflect.ValueOf(&s).Elem()
118		err := printfIntoProperty(v, testCase.val)
119		if err != nil && !testCase.err {
120			t.Errorf("unexpected error %s", err)
121		} else if err == nil && testCase.err {
122			t.Errorf("expected error")
123		} else if err == nil && v.String() != testCase.out {
124			t.Errorf("expected %q got %q", testCase.out, v.String())
125		}
126	}
127}
128
129type testProductVariableModule struct {
130	ModuleBase
131}
132
133func (m *testProductVariableModule) GenerateAndroidBuildActions(ctx ModuleContext) {
134}
135
136var testProductVariableProperties = struct {
137	Product_variables struct {
138		Eng struct {
139			Srcs   []string
140			Cflags []string
141		}
142	}
143}{}
144
145func testProductVariableModuleFactoryFactory(props interface{}) func() Module {
146	return func() Module {
147		m := &testProductVariableModule{}
148		clonedProps := proptools.CloneProperties(reflect.ValueOf(props)).Interface()
149		m.AddProperties(clonedProps)
150
151		// Set a default soongConfigVariableProperties, this will be used as the input to the property struct filter
152		// for this test module.
153		m.variableProperties = testProductVariableProperties
154		InitAndroidModule(m)
155		return m
156	}
157}
158
159func TestProductVariables(t *testing.T) {
160	// Test that a module can use one product variable even if it doesn't have all the properties
161	// supported by that product variable.
162	bp := `
163		module1 {
164			name: "foo",
165			product_variables: {
166				eng: {
167					srcs: ["foo.c"],
168				},
169			},
170		}
171		module2 {
172			name: "bar",
173			product_variables: {
174				eng: {
175					cflags: ["-DBAR"],
176				},
177			},
178		}
179
180		module3 {
181			name: "baz",
182		}
183	`
184
185	GroupFixturePreparers(
186		FixtureModifyProductVariables(func(variables FixtureProductVariables) {
187			variables.Eng = proptools.BoolPtr(true)
188		}),
189		FixtureRegisterWithContext(func(ctx RegistrationContext) {
190			// A module type that has a srcs property but not a cflags property.
191			ctx.RegisterModuleType("module1", testProductVariableModuleFactoryFactory(&struct {
192				Srcs []string
193			}{}))
194			// A module type that has a cflags property but not a srcs property.
195			ctx.RegisterModuleType("module2", testProductVariableModuleFactoryFactory(&struct {
196				Cflags []string
197			}{}))
198			// A module type that does not have any properties that match product_variables.
199			ctx.RegisterModuleType("module3", testProductVariableModuleFactoryFactory(&struct {
200				Foo []string
201			}{}))
202			ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
203				ctx.BottomUp("variable", VariableMutator).Parallel()
204			})
205		}),
206		FixtureWithRootAndroidBp(bp),
207	).RunTest(t)
208}
209
210var testProductVariableDefaultsProperties = struct {
211	Product_variables struct {
212		Eng struct {
213			Foo []string
214			Bar []string
215		}
216	}
217}{}
218
219type productVariablesDefaultsTestProperties struct {
220	Foo []string
221}
222
223type productVariablesDefaultsTestProperties2 struct {
224	Foo []string
225	Bar []string
226}
227
228type productVariablesDefaultsTestModule struct {
229	ModuleBase
230	DefaultableModuleBase
231	properties productVariablesDefaultsTestProperties
232}
233
234func (d *productVariablesDefaultsTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
235	ctx.Build(pctx, BuildParams{
236		Rule:   Touch,
237		Output: PathForModuleOut(ctx, "out"),
238	})
239}
240
241func productVariablesDefaultsTestModuleFactory() Module {
242	module := &productVariablesDefaultsTestModule{}
243	module.AddProperties(&module.properties)
244	module.variableProperties = testProductVariableDefaultsProperties
245	InitAndroidModule(module)
246	InitDefaultableModule(module)
247	return module
248}
249
250type productVariablesDefaultsTestDefaults struct {
251	ModuleBase
252	DefaultsModuleBase
253}
254
255func productVariablesDefaultsTestDefaultsFactory() Module {
256	defaults := &productVariablesDefaultsTestDefaults{}
257	defaults.AddProperties(&productVariablesDefaultsTestProperties{})
258	defaults.AddProperties(&productVariablesDefaultsTestProperties2{})
259	defaults.variableProperties = testProductVariableDefaultsProperties
260	InitDefaultsModule(defaults)
261	return defaults
262}
263
264// Test a defaults module that supports more product variable properties than the target module.
265func TestProductVariablesDefaults(t *testing.T) {
266	bp := `
267		defaults {
268			name: "defaults",
269			product_variables: {
270				eng: {
271					foo: ["product_variable_defaults"],
272					bar: ["product_variable_defaults"],
273				},
274			},
275			foo: ["defaults"],
276			bar: ["defaults"],
277		}
278
279		test {
280			name: "foo",
281			defaults: ["defaults"],
282			foo: ["module"],
283			product_variables: {
284				eng: {
285					foo: ["product_variable_module"],
286				},
287			},
288		}
289	`
290
291	result := GroupFixturePreparers(
292		FixtureModifyProductVariables(func(variables FixtureProductVariables) {
293			variables.Eng = boolPtr(true)
294		}),
295		PrepareForTestWithDefaults,
296		PrepareForTestWithVariables,
297		FixtureRegisterWithContext(func(ctx RegistrationContext) {
298			ctx.RegisterModuleType("test", productVariablesDefaultsTestModuleFactory)
299			ctx.RegisterModuleType("defaults", productVariablesDefaultsTestDefaultsFactory)
300		}),
301		FixtureWithRootAndroidBp(bp),
302	).RunTest(t)
303
304	foo := result.ModuleForTests("foo", "").Module().(*productVariablesDefaultsTestModule)
305
306	want := []string{"defaults", "module", "product_variable_defaults", "product_variable_module"}
307	AssertDeepEquals(t, "foo", want, foo.properties.Foo)
308}
309
310func BenchmarkSliceToTypeArray(b *testing.B) {
311	for _, n := range []int{1, 2, 4, 8, 100} {
312		var propStructs []interface{}
313		for i := 0; i < n; i++ {
314			propStructs = append(propStructs, &struct {
315				A *string
316				B string
317			}{})
318
319		}
320		b.Run(strconv.Itoa(n), func(b *testing.B) {
321			for i := 0; i < b.N; i++ {
322				_ = sliceToTypeArray(propStructs)
323			}
324		})
325	}
326}
327