• 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	"fmt"
19	"testing"
20)
21
22type soongConfigTestDefaultsModule struct {
23	ModuleBase
24	DefaultsModuleBase
25}
26
27func soongConfigTestDefaultsModuleFactory() Module {
28	m := &soongConfigTestDefaultsModule{}
29	m.AddProperties(&soongConfigTestModuleProperties{})
30	InitDefaultsModule(m)
31	return m
32}
33
34type soongConfigTestModule struct {
35	ModuleBase
36	DefaultableModuleBase
37	props soongConfigTestModuleProperties
38}
39
40type soongConfigTestModuleProperties struct {
41	Cflags []string
42}
43
44func soongConfigTestModuleFactory() Module {
45	m := &soongConfigTestModule{}
46	m.AddProperties(&m.props)
47	InitAndroidModule(m)
48	InitDefaultableModule(m)
49	return m
50}
51
52func (t soongConfigTestModule) GenerateAndroidBuildActions(ModuleContext) {}
53
54var prepareForSoongConfigTestModule = FixtureRegisterWithContext(func(ctx RegistrationContext) {
55	ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
56	ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
57})
58
59func TestSoongConfigModule(t *testing.T) {
60	configBp := `
61		soong_config_module_type {
62			name: "acme_test",
63			module_type: "test",
64			config_namespace: "acme",
65			variables: ["board", "feature1", "FEATURE3", "unused_string_var"],
66			bool_variables: ["feature2", "unused_feature", "always_true"],
67			value_variables: ["size", "unused_size"],
68			properties: ["cflags", "srcs", "defaults"],
69		}
70
71		soong_config_string_variable {
72			name: "board",
73			values: ["soc_a", "soc_b", "soc_c", "soc_d"],
74		}
75
76		soong_config_string_variable {
77			name: "unused_string_var",
78			values: ["a", "b"],
79		}
80
81		soong_config_bool_variable {
82			name: "feature1",
83		}
84
85		soong_config_bool_variable {
86			name: "FEATURE3",
87		}
88	`
89
90	importBp := `
91		soong_config_module_type_import {
92			from: "SoongConfig.bp",
93			module_types: ["acme_test"],
94		}
95	`
96
97	bp := `
98		test_defaults {
99			name: "foo_defaults",
100			cflags: ["DEFAULT"],
101		}
102
103		acme_test {
104			name: "foo",
105			cflags: ["-DGENERIC"],
106			defaults: ["foo_defaults"],
107			soong_config_variables: {
108				board: {
109					soc_a: {
110						cflags: ["-DSOC_A"],
111					},
112					soc_b: {
113						cflags: ["-DSOC_B"],
114					},
115					soc_c: {},
116					conditions_default: {
117						cflags: ["-DSOC_CONDITIONS_DEFAULT"],
118					},
119				},
120				size: {
121					cflags: ["-DSIZE=%s"],
122					conditions_default: {
123						cflags: ["-DSIZE=CONDITIONS_DEFAULT"],
124					},
125				},
126				feature1: {
127					  conditions_default: {
128						  cflags: ["-DF1_CONDITIONS_DEFAULT"],
129					  },
130					cflags: ["-DFEATURE1"],
131				},
132				feature2: {
133					cflags: ["-DFEATURE2"],
134					 conditions_default: {
135						 cflags: ["-DF2_CONDITIONS_DEFAULT"],
136					 },
137				},
138				FEATURE3: {
139					cflags: ["-DFEATURE3"],
140				},
141			},
142		}
143
144		test_defaults {
145			name: "foo_defaults_a",
146			cflags: ["DEFAULT_A"],
147		}
148
149		test_defaults {
150			name: "foo_defaults_b",
151			cflags: ["DEFAULT_B"],
152		}
153
154		test_defaults {
155			name: "foo_defaults_always_true",
156			cflags: ["DEFAULT_ALWAYS_TRUE"],
157		}
158
159		acme_test {
160			name: "foo_with_defaults",
161			cflags: ["-DGENERIC"],
162			defaults: ["foo_defaults"],
163			soong_config_variables: {
164				board: {
165					soc_a: {
166						cflags: ["-DSOC_A"],
167						defaults: ["foo_defaults_a"],
168					},
169					soc_b: {
170						cflags: ["-DSOC_B"],
171						defaults: ["foo_defaults_b"],
172					},
173					soc_c: {},
174				},
175				size: {
176					cflags: ["-DSIZE=%s"],
177				},
178				feature1: {
179					cflags: ["-DFEATURE1"],
180				},
181				feature2: {
182					cflags: ["-DFEATURE2"],
183				},
184				FEATURE3: {
185					cflags: ["-DFEATURE3"],
186				},
187				always_true: {
188					defaults: ["foo_defaults_always_true"],
189					conditions_default: {
190						// verify that conditions_default is skipped if the
191						// soong config variable is true by specifying a
192						// non-existent module in conditions_default
193						defaults: ["//nonexistent:defaults"],
194					}
195				},
196			},
197		}
198    `
199
200	fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer {
201		return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
202			variables.VendorVars = vars
203		})
204	}
205
206	run := func(t *testing.T, bp string, fs MockFS) {
207		testCases := []struct {
208			name                     string
209			preparer                 FixturePreparer
210			fooExpectedFlags         []string
211			fooDefaultsExpectedFlags []string
212		}{
213			{
214				name: "withValues",
215				preparer: fixtureForVendorVars(map[string]map[string]string{
216					"acme": {
217						"board":    "soc_a",
218						"size":     "42",
219						"feature1": "true",
220						"feature2": "false",
221						// FEATURE3 unset
222						"unused_feature":    "true", // unused
223						"unused_size":       "1",    // unused
224						"unused_string_var": "a",    // unused
225						"always_true":       "true",
226					},
227				}),
228				fooExpectedFlags: []string{
229					"DEFAULT",
230					"-DGENERIC",
231					"-DF2_CONDITIONS_DEFAULT",
232					"-DSIZE=42",
233					"-DSOC_A",
234					"-DFEATURE1",
235				},
236				fooDefaultsExpectedFlags: []string{
237					"DEFAULT_A",
238					"DEFAULT_ALWAYS_TRUE",
239					"DEFAULT",
240					"-DGENERIC",
241					"-DSIZE=42",
242					"-DSOC_A",
243					"-DFEATURE1",
244				},
245			},
246			{
247				name: "empty_prop_for_string_var",
248				preparer: fixtureForVendorVars(map[string]map[string]string{
249					"acme": {
250						"board":       "soc_c",
251						"always_true": "true",
252					}}),
253				fooExpectedFlags: []string{
254					"DEFAULT",
255					"-DGENERIC",
256					"-DF2_CONDITIONS_DEFAULT",
257					"-DSIZE=CONDITIONS_DEFAULT",
258					"-DF1_CONDITIONS_DEFAULT",
259				},
260				fooDefaultsExpectedFlags: []string{
261					"DEFAULT_ALWAYS_TRUE",
262					"DEFAULT",
263					"-DGENERIC",
264				},
265			},
266			{
267				name: "unused_string_var",
268				preparer: fixtureForVendorVars(map[string]map[string]string{
269					"acme": {
270						"board":       "soc_d",
271						"always_true": "true",
272					}}),
273				fooExpectedFlags: []string{
274					"DEFAULT",
275					"-DGENERIC",
276					"-DF2_CONDITIONS_DEFAULT",
277					"-DSIZE=CONDITIONS_DEFAULT",
278					"-DSOC_CONDITIONS_DEFAULT", // foo does not contain a prop "soc_d", so we use the default
279					"-DF1_CONDITIONS_DEFAULT",
280				},
281				fooDefaultsExpectedFlags: []string{
282					"DEFAULT_ALWAYS_TRUE",
283					"DEFAULT",
284					"-DGENERIC",
285				},
286			},
287
288			{
289				name: "conditions_default",
290				preparer: fixtureForVendorVars(map[string]map[string]string{
291					"acme": {
292						"always_true": "true",
293					}}),
294				fooExpectedFlags: []string{
295					"DEFAULT",
296					"-DGENERIC",
297					"-DF2_CONDITIONS_DEFAULT",
298					"-DSIZE=CONDITIONS_DEFAULT",
299					"-DSOC_CONDITIONS_DEFAULT",
300					"-DF1_CONDITIONS_DEFAULT",
301				},
302				fooDefaultsExpectedFlags: []string{
303					"DEFAULT_ALWAYS_TRUE",
304					"DEFAULT",
305					"-DGENERIC",
306				},
307			},
308		}
309
310		for _, tc := range testCases {
311			t.Run(tc.name, func(t *testing.T) {
312				result := GroupFixturePreparers(
313					tc.preparer,
314					PrepareForTestWithDefaults,
315					PrepareForTestWithSoongConfigModuleBuildComponents,
316					prepareForSoongConfigTestModule,
317					fs.AddToFixture(),
318					FixtureWithRootAndroidBp(bp),
319				).RunTest(t)
320
321				foo := result.ModuleForTests("foo", "").Module().(*soongConfigTestModule)
322				AssertDeepEquals(t, "foo cflags", tc.fooExpectedFlags, foo.props.Cflags)
323
324				fooDefaults := result.ModuleForTests("foo_with_defaults", "").Module().(*soongConfigTestModule)
325				AssertDeepEquals(t, "foo_with_defaults cflags", tc.fooDefaultsExpectedFlags, fooDefaults.props.Cflags)
326			})
327		}
328	}
329
330	t.Run("single file", func(t *testing.T) {
331		run(t, configBp+bp, nil)
332	})
333
334	t.Run("import", func(t *testing.T) {
335		run(t, importBp+bp, map[string][]byte{
336			"SoongConfig.bp": []byte(configBp),
337		})
338	})
339}
340
341func TestNonExistentPropertyInSoongConfigModule(t *testing.T) {
342	bp := `
343		soong_config_module_type {
344			name: "acme_test",
345			module_type: "test",
346			config_namespace: "acme",
347			bool_variables: ["feature1"],
348			properties: ["made_up_property"],
349		}
350
351		acme_test {
352			name: "foo",
353			cflags: ["-DGENERIC"],
354			soong_config_variables: {
355				feature1: {
356					made_up_property: true,
357				},
358			},
359		}
360    `
361
362	fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer {
363		return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
364			variables.VendorVars = vars
365		})
366	}
367
368	GroupFixturePreparers(
369		fixtureForVendorVars(map[string]map[string]string{"acme": {"feature1": "1"}}),
370		PrepareForTestWithDefaults,
371		PrepareForTestWithSoongConfigModuleBuildComponents,
372		prepareForSoongConfigTestModule,
373		FixtureWithRootAndroidBp(bp),
374	).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{
375		// TODO(b/171232169): improve the error message for non-existent properties
376		`unrecognized property "soong_config_variables`,
377	})).RunTest(t)
378}
379
380func TestDuplicateStringValueInSoongConfigStringVariable(t *testing.T) {
381	bp := `
382		soong_config_string_variable {
383			name: "board",
384			values: ["soc_a", "soc_b", "soc_c", "soc_a"],
385		}
386
387		soong_config_module_type {
388			name: "acme_test",
389			module_type: "test",
390			config_namespace: "acme",
391			variables: ["board"],
392			properties: ["cflags", "srcs", "defaults"],
393		}
394    `
395
396	fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer {
397		return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
398			variables.VendorVars = vars
399		})
400	}
401
402	GroupFixturePreparers(
403		fixtureForVendorVars(map[string]map[string]string{"acme": {"feature1": "1"}}),
404		PrepareForTestWithDefaults,
405		PrepareForTestWithSoongConfigModuleBuildComponents,
406		prepareForSoongConfigTestModule,
407		FixtureWithRootAndroidBp(bp),
408	).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{
409		// TODO(b/171232169): improve the error message for non-existent properties
410		`Android.bp: soong_config_string_variable: values property error: duplicate value: "soc_a"`,
411	})).RunTest(t)
412}
413
414type soongConfigTestSingletonModule struct {
415	SingletonModuleBase
416	props soongConfigTestSingletonModuleProperties
417}
418
419type soongConfigTestSingletonModuleProperties struct {
420	Fragments []struct {
421		Apex   string
422		Module string
423	}
424}
425
426func soongConfigTestSingletonModuleFactory() SingletonModule {
427	m := &soongConfigTestSingletonModule{}
428	m.AddProperties(&m.props)
429	InitAndroidModule(m)
430	return m
431}
432
433func (t *soongConfigTestSingletonModule) GenerateAndroidBuildActions(ModuleContext) {}
434
435func (t *soongConfigTestSingletonModule) GenerateSingletonBuildActions(SingletonContext) {}
436
437var prepareForSoongConfigTestSingletonModule = FixtureRegisterWithContext(func(ctx RegistrationContext) {
438	ctx.RegisterSingletonModuleType("test_singleton", soongConfigTestSingletonModuleFactory)
439})
440
441func TestSoongConfigModuleSingletonModule(t *testing.T) {
442	bp := `
443		soong_config_module_type {
444			name: "acme_test_singleton",
445			module_type: "test_singleton",
446			config_namespace: "acme",
447			bool_variables: ["coyote"],
448			properties: ["fragments"],
449		}
450
451		acme_test_singleton {
452			name: "wiley",
453			fragments: [
454				{
455					apex: "com.android.acme",
456					module: "road-runner",
457				},
458			],
459			soong_config_variables: {
460				coyote: {
461					fragments: [
462						{
463							apex: "com.android.acme",
464							module: "wiley",
465						},
466					],
467				},
468			},
469		}
470	`
471
472	for _, test := range []struct {
473		coyote            bool
474		expectedFragments string
475	}{
476		{
477			coyote:            false,
478			expectedFragments: "[{Apex:com.android.acme Module:road-runner}]",
479		},
480		{
481			coyote:            true,
482			expectedFragments: "[{Apex:com.android.acme Module:road-runner} {Apex:com.android.acme Module:wiley}]",
483		},
484	} {
485		t.Run(fmt.Sprintf("coyote:%t", test.coyote), func(t *testing.T) {
486			result := GroupFixturePreparers(
487				PrepareForTestWithSoongConfigModuleBuildComponents,
488				prepareForSoongConfigTestSingletonModule,
489				FixtureWithRootAndroidBp(bp),
490				FixtureModifyProductVariables(func(variables FixtureProductVariables) {
491					variables.VendorVars = map[string]map[string]string{
492						"acme": {
493							"coyote": fmt.Sprintf("%t", test.coyote),
494						},
495					}
496				}),
497			).RunTest(t)
498
499			// Make sure that the singleton was created.
500			result.SingletonForTests("test_singleton")
501			m := result.ModuleForTests("wiley", "").module.(*soongConfigTestSingletonModule)
502			AssertStringEquals(t, "fragments", test.expectedFragments, fmt.Sprintf("%+v", m.props.Fragments))
503		})
504	}
505}
506