• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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", "always_true"],
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		test_defaults {
152			name: "foo_defaults_always_true",
153			cflags: ["DEFAULT_ALWAYS_TRUE"],
154		}
155
156		acme_test {
157			name: "foo_with_defaults",
158			cflags: ["-DGENERIC"],
159			defaults: ["foo_defaults"],
160			soong_config_variables: {
161				board: {
162					soc_a: {
163						cflags: ["-DSOC_A"],
164						defaults: ["foo_defaults_a"],
165					},
166					soc_b: {
167						cflags: ["-DSOC_B"],
168						defaults: ["foo_defaults_b"],
169					},
170					soc_c: {},
171				},
172				size: {
173					cflags: ["-DSIZE=%s"],
174				},
175				feature1: {
176					cflags: ["-DFEATURE1"],
177				},
178				feature2: {
179					cflags: ["-DFEATURE2"],
180				},
181				FEATURE3: {
182					cflags: ["-DFEATURE3"],
183				},
184				always_true: {
185					defaults: ["foo_defaults_always_true"],
186					conditions_default: {
187						// verify that conditions_default is skipped if the
188						// soong config variable is true by specifying a
189						// non-existent module in conditions_default
190						defaults: ["//nonexistent:defaults"],
191					}
192				},
193			},
194		}
195    `
196
197	fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer {
198		return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
199			variables.VendorVars = vars
200		})
201	}
202
203	run := func(t *testing.T, bp string, fs MockFS) {
204		testCases := []struct {
205			name                     string
206			preparer                 FixturePreparer
207			fooExpectedFlags         []string
208			fooDefaultsExpectedFlags []string
209		}{
210			{
211				name: "withValues",
212				preparer: fixtureForVendorVars(map[string]map[string]string{
213					"acme": {
214						"board":    "soc_a",
215						"size":     "42",
216						"feature1": "true",
217						"feature2": "false",
218						// FEATURE3 unset
219						"unused_feature":    "true", // unused
220						"unused_size":       "1",    // unused
221						"unused_string_var": "a",    // unused
222						"always_true":       "true",
223					},
224				}),
225				fooExpectedFlags: []string{
226					"DEFAULT",
227					"-DGENERIC",
228					"-DF2_CONDITIONS_DEFAULT",
229					"-DSIZE=42",
230					"-DSOC_A",
231					"-DFEATURE1",
232				},
233				fooDefaultsExpectedFlags: []string{
234					"DEFAULT_A",
235					"DEFAULT_ALWAYS_TRUE",
236					"DEFAULT",
237					"-DGENERIC",
238					"-DSIZE=42",
239					"-DSOC_A",
240					"-DFEATURE1",
241				},
242			},
243			{
244				name: "empty_prop_for_string_var",
245				preparer: fixtureForVendorVars(map[string]map[string]string{
246					"acme": {
247						"board":       "soc_c",
248						"always_true": "true",
249					}}),
250				fooExpectedFlags: []string{
251					"DEFAULT",
252					"-DGENERIC",
253					"-DF2_CONDITIONS_DEFAULT",
254					"-DSIZE=CONDITIONS_DEFAULT",
255					"-DF1_CONDITIONS_DEFAULT",
256				},
257				fooDefaultsExpectedFlags: []string{
258					"DEFAULT_ALWAYS_TRUE",
259					"DEFAULT",
260					"-DGENERIC",
261				},
262			},
263			{
264				name: "unused_string_var",
265				preparer: fixtureForVendorVars(map[string]map[string]string{
266					"acme": {
267						"board":       "soc_d",
268						"always_true": "true",
269					}}),
270				fooExpectedFlags: []string{
271					"DEFAULT",
272					"-DGENERIC",
273					"-DF2_CONDITIONS_DEFAULT",
274					"-DSIZE=CONDITIONS_DEFAULT",
275					"-DSOC_CONDITIONS_DEFAULT", // foo does not contain a prop "soc_d", so we use the default
276					"-DF1_CONDITIONS_DEFAULT",
277				},
278				fooDefaultsExpectedFlags: []string{
279					"DEFAULT_ALWAYS_TRUE",
280					"DEFAULT",
281					"-DGENERIC",
282				},
283			},
284
285			{
286				name: "conditions_default",
287				preparer: fixtureForVendorVars(map[string]map[string]string{
288					"acme": {
289						"always_true": "true",
290					}}),
291				fooExpectedFlags: []string{
292					"DEFAULT",
293					"-DGENERIC",
294					"-DF2_CONDITIONS_DEFAULT",
295					"-DSIZE=CONDITIONS_DEFAULT",
296					"-DSOC_CONDITIONS_DEFAULT",
297					"-DF1_CONDITIONS_DEFAULT",
298				},
299				fooDefaultsExpectedFlags: []string{
300					"DEFAULT_ALWAYS_TRUE",
301					"DEFAULT",
302					"-DGENERIC",
303				},
304			},
305		}
306
307		for _, tc := range testCases {
308			t.Run(tc.name, func(t *testing.T) {
309				result := GroupFixturePreparers(
310					tc.preparer,
311					PrepareForTestWithDefaults,
312					FixtureRegisterWithContext(func(ctx RegistrationContext) {
313						ctx.RegisterModuleType("soong_config_module_type_import", SoongConfigModuleTypeImportFactory)
314						ctx.RegisterModuleType("soong_config_module_type", SoongConfigModuleTypeFactory)
315						ctx.RegisterModuleType("soong_config_string_variable", SoongConfigStringVariableDummyFactory)
316						ctx.RegisterModuleType("soong_config_bool_variable", SoongConfigBoolVariableDummyFactory)
317						ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
318						ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
319					}),
320					fs.AddToFixture(),
321					FixtureWithRootAndroidBp(bp),
322				).RunTest(t)
323
324				foo := result.ModuleForTests("foo", "").Module().(*soongConfigTestModule)
325				AssertDeepEquals(t, "foo cflags", tc.fooExpectedFlags, foo.props.Cflags)
326
327				fooDefaults := result.ModuleForTests("foo_with_defaults", "").Module().(*soongConfigTestModule)
328				AssertDeepEquals(t, "foo_with_defaults cflags", tc.fooDefaultsExpectedFlags, fooDefaults.props.Cflags)
329			})
330		}
331	}
332
333	t.Run("single file", func(t *testing.T) {
334		run(t, configBp+bp, nil)
335	})
336
337	t.Run("import", func(t *testing.T) {
338		run(t, importBp+bp, map[string][]byte{
339			"SoongConfig.bp": []byte(configBp),
340		})
341	})
342}
343
344func TestNonExistentPropertyInSoongConfigModule(t *testing.T) {
345	bp := `
346		soong_config_module_type {
347			name: "acme_test",
348			module_type: "test",
349			config_namespace: "acme",
350			bool_variables: ["feature1"],
351			properties: ["made_up_property"],
352		}
353
354		acme_test {
355			name: "foo",
356			cflags: ["-DGENERIC"],
357			soong_config_variables: {
358				feature1: {
359					made_up_property: true,
360				},
361			},
362		}
363    `
364
365	fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer {
366		return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
367			variables.VendorVars = vars
368		})
369	}
370
371	GroupFixturePreparers(
372		fixtureForVendorVars(map[string]map[string]string{"acme": {"feature1": "1"}}),
373		PrepareForTestWithDefaults,
374		FixtureRegisterWithContext(func(ctx RegistrationContext) {
375			ctx.RegisterModuleType("soong_config_module_type_import", SoongConfigModuleTypeImportFactory)
376			ctx.RegisterModuleType("soong_config_module_type", SoongConfigModuleTypeFactory)
377			ctx.RegisterModuleType("soong_config_string_variable", SoongConfigStringVariableDummyFactory)
378			ctx.RegisterModuleType("soong_config_bool_variable", SoongConfigBoolVariableDummyFactory)
379			ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
380			ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
381		}),
382		FixtureWithRootAndroidBp(bp),
383	).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{
384		// TODO(b/171232169): improve the error message for non-existent properties
385		`unrecognized property "soong_config_variables`,
386	})).RunTest(t)
387}
388
389func TestDuplicateStringValueInSoongConfigStringVariable(t *testing.T) {
390	bp := `
391		soong_config_string_variable {
392			name: "board",
393			values: ["soc_a", "soc_b", "soc_c", "soc_a"],
394		}
395
396		soong_config_module_type {
397			name: "acme_test",
398			module_type: "test",
399			config_namespace: "acme",
400			variables: ["board"],
401			properties: ["cflags", "srcs", "defaults"],
402		}
403    `
404
405	fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer {
406		return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
407			variables.VendorVars = vars
408		})
409	}
410
411	GroupFixturePreparers(
412		fixtureForVendorVars(map[string]map[string]string{"acme": {"feature1": "1"}}),
413		PrepareForTestWithDefaults,
414		FixtureRegisterWithContext(func(ctx RegistrationContext) {
415			ctx.RegisterModuleType("soong_config_module_type_import", SoongConfigModuleTypeImportFactory)
416			ctx.RegisterModuleType("soong_config_module_type", SoongConfigModuleTypeFactory)
417			ctx.RegisterModuleType("soong_config_string_variable", SoongConfigStringVariableDummyFactory)
418			ctx.RegisterModuleType("soong_config_bool_variable", SoongConfigBoolVariableDummyFactory)
419			ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
420			ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
421		}),
422		FixtureWithRootAndroidBp(bp),
423	).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{
424		// TODO(b/171232169): improve the error message for non-existent properties
425		`Android.bp: soong_config_string_variable: values property error: duplicate value: "soc_a"`,
426	})).RunTest(t)
427}
428
429func testConfigWithVendorVars(buildDir, bp string, fs map[string][]byte, vendorVars map[string]map[string]string) Config {
430	config := TestConfig(buildDir, nil, bp, fs)
431
432	config.TestProductVariables.VendorVars = vendorVars
433
434	return config
435}
436