1// Copyright 2019 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	"testing"
19)
20
21type soongConfigTestDefaultsModuleProperties struct {
22}
23
24type soongConfigTestDefaultsModule struct {
25	ModuleBase
26	DefaultsModuleBase
27}
28
29func soongConfigTestDefaultsModuleFactory() Module {
30	m := &soongConfigTestDefaultsModule{}
31	m.AddProperties(&soongConfigTestModuleProperties{})
32	InitDefaultsModule(m)
33	return m
34}
35
36type soongConfigTestModule struct {
37	ModuleBase
38	DefaultableModuleBase
39	props soongConfigTestModuleProperties
40}
41
42type soongConfigTestModuleProperties struct {
43	Cflags []string
44}
45
46func soongConfigTestModuleFactory() Module {
47	m := &soongConfigTestModule{}
48	m.AddProperties(&m.props)
49	InitAndroidModule(m)
50	InitDefaultableModule(m)
51	return m
52}
53
54func (t soongConfigTestModule) GenerateAndroidBuildActions(ModuleContext) {}
55
56func TestSoongConfigModule(t *testing.T) {
57	configBp := `
58		soong_config_module_type {
59			name: "acme_test",
60			module_type: "test",
61			config_namespace: "acme",
62			variables: ["board", "feature1", "FEATURE3", "unused_string_var"],
63			bool_variables: ["feature2", "unused_feature"],
64			value_variables: ["size", "unused_size"],
65			properties: ["cflags", "srcs", "defaults"],
66		}
67
68		soong_config_string_variable {
69			name: "board",
70			values: ["soc_a", "soc_b", "soc_c", "soc_d"],
71		}
72
73		soong_config_string_variable {
74			name: "unused_string_var",
75			values: ["a", "b"],
76		}
77
78		soong_config_bool_variable {
79			name: "feature1",
80		}
81
82		soong_config_bool_variable {
83			name: "FEATURE3",
84		}
85	`
86
87	importBp := `
88		soong_config_module_type_import {
89			from: "SoongConfig.bp",
90			module_types: ["acme_test"],
91		}
92	`
93
94	bp := `
95		test_defaults {
96			name: "foo_defaults",
97			cflags: ["DEFAULT"],
98		}
99
100		acme_test {
101			name: "foo",
102			cflags: ["-DGENERIC"],
103			defaults: ["foo_defaults"],
104			soong_config_variables: {
105				board: {
106					soc_a: {
107						cflags: ["-DSOC_A"],
108					},
109					soc_b: {
110						cflags: ["-DSOC_B"],
111					},
112					soc_c: {},
113					conditions_default: {
114						cflags: ["-DSOC_CONDITIONS_DEFAULT"],
115					},
116				},
117				size: {
118					cflags: ["-DSIZE=%s"],
119					conditions_default: {
120						cflags: ["-DSIZE=CONDITIONS_DEFAULT"],
121					},
122				},
123				feature1: {
124					  conditions_default: {
125						  cflags: ["-DF1_CONDITIONS_DEFAULT"],
126					  },
127					cflags: ["-DFEATURE1"],
128				},
129				feature2: {
130					cflags: ["-DFEATURE2"],
131					 conditions_default: {
132						 cflags: ["-DF2_CONDITIONS_DEFAULT"],
133					 },
134				},
135				FEATURE3: {
136					cflags: ["-DFEATURE3"],
137				},
138			},
139		}
140
141		test_defaults {
142			name: "foo_defaults_a",
143			cflags: ["DEFAULT_A"],
144		}
145
146		test_defaults {
147			name: "foo_defaults_b",
148			cflags: ["DEFAULT_B"],
149		}
150
151		acme_test {
152			name: "foo_with_defaults",
153			cflags: ["-DGENERIC"],
154			defaults: ["foo_defaults"],
155			soong_config_variables: {
156				board: {
157					soc_a: {
158						cflags: ["-DSOC_A"],
159						defaults: ["foo_defaults_a"],
160					},
161					soc_b: {
162						cflags: ["-DSOC_B"],
163						defaults: ["foo_defaults_b"],
164					},
165					soc_c: {},
166				},
167				size: {
168					cflags: ["-DSIZE=%s"],
169				},
170				feature1: {
171					cflags: ["-DFEATURE1"],
172				},
173				feature2: {
174					cflags: ["-DFEATURE2"],
175				},
176				FEATURE3: {
177					cflags: ["-DFEATURE3"],
178				},
179			},
180		}
181    `
182
183	fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer {
184		return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
185			variables.VendorVars = vars
186		})
187	}
188
189	run := func(t *testing.T, bp string, fs MockFS) {
190		testCases := []struct {
191			name                     string
192			preparer                 FixturePreparer
193			fooExpectedFlags         []string
194			fooDefaultsExpectedFlags []string
195		}{
196			{
197				name: "withValues",
198				preparer: fixtureForVendorVars(map[string]map[string]string{
199					"acme": {
200						"board":    "soc_a",
201						"size":     "42",
202						"feature1": "true",
203						"feature2": "false",
204						// FEATURE3 unset
205						"unused_feature":    "true", // unused
206						"unused_size":       "1",    // unused
207						"unused_string_var": "a",    // unused
208					},
209				}),
210				fooExpectedFlags: []string{
211					"DEFAULT",
212					"-DGENERIC",
213					"-DF2_CONDITIONS_DEFAULT",
214					"-DSIZE=42",
215					"-DSOC_A",
216					"-DFEATURE1",
217				},
218				fooDefaultsExpectedFlags: []string{
219					"DEFAULT_A",
220					"DEFAULT",
221					"-DGENERIC",
222					"-DSIZE=42",
223					"-DSOC_A",
224					"-DFEATURE1",
225				},
226			},
227			{
228				name: "empty_prop_for_string_var",
229				preparer: fixtureForVendorVars(map[string]map[string]string{
230					"acme": {"board": "soc_c"}}),
231				fooExpectedFlags: []string{
232					"DEFAULT",
233					"-DGENERIC",
234					"-DF2_CONDITIONS_DEFAULT",
235					"-DSIZE=CONDITIONS_DEFAULT",
236					"-DF1_CONDITIONS_DEFAULT",
237				},
238				fooDefaultsExpectedFlags: []string{
239					"DEFAULT",
240					"-DGENERIC",
241				},
242			},
243			{
244				name: "unused_string_var",
245				preparer: fixtureForVendorVars(map[string]map[string]string{
246					"acme": {"board": "soc_d"}}),
247				fooExpectedFlags: []string{
248					"DEFAULT",
249					"-DGENERIC",
250					"-DF2_CONDITIONS_DEFAULT",
251					"-DSIZE=CONDITIONS_DEFAULT",
252					"-DSOC_CONDITIONS_DEFAULT", // foo does not contain a prop "soc_d", so we use the default
253					"-DF1_CONDITIONS_DEFAULT",
254				},
255				fooDefaultsExpectedFlags: []string{
256					"DEFAULT",
257					"-DGENERIC",
258				},
259			},
260
261			{
262				name:     "conditions_default",
263				preparer: fixtureForVendorVars(map[string]map[string]string{}),
264				fooExpectedFlags: []string{
265					"DEFAULT",
266					"-DGENERIC",
267					"-DF2_CONDITIONS_DEFAULT",
268					"-DSIZE=CONDITIONS_DEFAULT",
269					"-DSOC_CONDITIONS_DEFAULT",
270					"-DF1_CONDITIONS_DEFAULT",
271				},
272				fooDefaultsExpectedFlags: []string{
273					"DEFAULT",
274					"-DGENERIC",
275				},
276			},
277		}
278
279		for _, tc := range testCases {
280			t.Run(tc.name, func(t *testing.T) {
281				result := GroupFixturePreparers(
282					tc.preparer,
283					PrepareForTestWithDefaults,
284					FixtureRegisterWithContext(func(ctx RegistrationContext) {
285						ctx.RegisterModuleType("soong_config_module_type_import", soongConfigModuleTypeImportFactory)
286						ctx.RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory)
287						ctx.RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory)
288						ctx.RegisterModuleType("soong_config_bool_variable", soongConfigBoolVariableDummyFactory)
289						ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
290						ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
291					}),
292					fs.AddToFixture(),
293					FixtureWithRootAndroidBp(bp),
294				).RunTest(t)
295
296				foo := result.ModuleForTests("foo", "").Module().(*soongConfigTestModule)
297				AssertDeepEquals(t, "foo cflags", tc.fooExpectedFlags, foo.props.Cflags)
298
299				fooDefaults := result.ModuleForTests("foo_with_defaults", "").Module().(*soongConfigTestModule)
300				AssertDeepEquals(t, "foo_with_defaults cflags", tc.fooDefaultsExpectedFlags, fooDefaults.props.Cflags)
301			})
302		}
303	}
304
305	t.Run("single file", func(t *testing.T) {
306		run(t, configBp+bp, nil)
307	})
308
309	t.Run("import", func(t *testing.T) {
310		run(t, importBp+bp, map[string][]byte{
311			"SoongConfig.bp": []byte(configBp),
312		})
313	})
314}
315
316func testConfigWithVendorVars(buildDir, bp string, fs map[string][]byte, vendorVars map[string]map[string]string) Config {
317	config := TestConfig(buildDir, nil, bp, fs)
318
319	config.TestProductVariables.VendorVars = vendorVars
320
321	return config
322}
323