• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2017 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	"path/filepath"
20	"reflect"
21	"strings"
22	"testing"
23)
24
25func validateConfigAnnotations(configurable jsonConfigurable) (err error) {
26	reflectType := reflect.TypeOf(configurable)
27	reflectType = reflectType.Elem()
28	for i := 0; i < reflectType.NumField(); i++ {
29		field := reflectType.Field(i)
30		jsonTag := field.Tag.Get("json")
31		// Check for mistakes in the json tag
32		if jsonTag != "" && !strings.HasPrefix(jsonTag, ",") {
33			if !strings.Contains(jsonTag, ",") {
34				// Probably an accidental rename, most likely "omitempty" instead of ",omitempty"
35				return fmt.Errorf("Field %s.%s has tag %s which specifies to change its json field name to %q.\n"+
36					"Did you mean to use an annotation of %q?\n"+
37					"(Alternatively, to change the json name of the field, rename the field in source instead.)",
38					reflectType.Name(), field.Name, field.Tag, jsonTag, ","+jsonTag)
39			} else {
40				// Although this rename was probably intentional,
41				// a json annotation is still more confusing than renaming the source variable
42				requestedName := strings.Split(jsonTag, ",")[0]
43				return fmt.Errorf("Field %s.%s has tag %s which specifies to change its json field name to %q.\n"+
44					"To change the json name of the field, rename the field in source instead.",
45					reflectType.Name(), field.Name, field.Tag, requestedName)
46
47			}
48		}
49	}
50	return nil
51}
52
53type configType struct {
54	PopulateMe *bool `json:"omitempty"`
55}
56
57func (c *configType) SetDefaultConfig() {
58}
59
60// tests that ValidateConfigAnnotation works
61func TestValidateConfigAnnotations(t *testing.T) {
62	config := configType{}
63	err := validateConfigAnnotations(&config)
64	expectedError := `Field configType.PopulateMe has tag json:"omitempty" which specifies to change its json field name to "omitempty".
65Did you mean to use an annotation of ",omitempty"?
66(Alternatively, to change the json name of the field, rename the field in source instead.)`
67	if err.Error() != expectedError {
68		t.Errorf("Incorrect error; expected:\n"+
69			"%s\n"+
70			"got:\n"+
71			"%s",
72			expectedError, err.Error())
73	}
74}
75
76// run validateConfigAnnotations against each type that might have json annotations
77func TestProductConfigAnnotations(t *testing.T) {
78	err := validateConfigAnnotations(&ProductVariables{})
79	if err != nil {
80		t.Error(err.Error())
81	}
82}
83
84func TestMissingVendorConfig(t *testing.T) {
85	c := &config{}
86	if c.VendorConfig("test").Bool("not_set") {
87		t.Errorf("Expected false")
88	}
89}
90
91func verifyProductVariableMarshaling(t *testing.T, v ProductVariables) {
92	dir := t.TempDir()
93	path := filepath.Join(dir, "test.variables")
94	err := saveToConfigFile(&v, path)
95	if err != nil {
96		t.Errorf("Couldn't save default product config: %q", err)
97	}
98
99	var v2 ProductVariables
100	err = loadFromConfigFile(&v2, path)
101	if err != nil {
102		t.Errorf("Couldn't load default product config: %q", err)
103	}
104}
105func TestDefaultProductVariableMarshaling(t *testing.T) {
106	v := ProductVariables{}
107	v.SetDefaultConfig()
108	verifyProductVariableMarshaling(t, v)
109}
110
111func TestBootJarsMarshaling(t *testing.T) {
112	v := ProductVariables{}
113	v.SetDefaultConfig()
114	v.BootJars = ConfiguredJarList{
115		apexes: []string{"apex"},
116		jars:   []string{"jar"},
117	}
118
119	verifyProductVariableMarshaling(t, v)
120}
121
122func assertStringEquals(t *testing.T, expected, actual string) {
123	if actual != expected {
124		t.Errorf("expected %q found %q", expected, actual)
125	}
126}
127
128func TestReleaseAconfigExtraReleaseConfigs(t *testing.T) {
129	testCases := []struct {
130		name     string
131		flag     string
132		expected []string
133	}{
134		{
135			name:     "empty",
136			flag:     "",
137			expected: []string{},
138		},
139		{
140			name:     "specified",
141			flag:     "bar foo",
142			expected: []string{"bar", "foo"},
143		},
144		{
145			name:     "duplicates",
146			flag:     "foo bar foo",
147			expected: []string{"foo", "bar"},
148		},
149	}
150
151	for _, tc := range testCases {
152		fixture := GroupFixturePreparers(
153			PrepareForTestWithBuildFlag("RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS", tc.flag),
154		)
155		actual := fixture.RunTest(t).Config.ReleaseAconfigExtraReleaseConfigs()
156		AssertArrayString(t, tc.name, tc.expected, actual)
157	}
158}
159
160func TestConfiguredJarList(t *testing.T) {
161	list1 := CreateTestConfiguredJarList([]string{"apex1:jarA"})
162
163	t.Run("create", func(t *testing.T) {
164		assertStringEquals(t, "apex1:jarA", list1.String())
165	})
166
167	t.Run("create invalid - missing apex", func(t *testing.T) {
168		defer func() {
169			err := recover().(error)
170			assertStringEquals(t, "malformed (apex, jar) pair: 'jarA', expected format: <apex>:<jar>", err.Error())
171		}()
172		CreateTestConfiguredJarList([]string{"jarA"})
173	})
174
175	t.Run("create invalid - empty apex", func(t *testing.T) {
176		defer func() {
177			err := recover().(error)
178			assertStringEquals(t, "invalid apex '' in <apex>:<jar> pair ':jarA', expected format: <apex>:<jar>", err.Error())
179		}()
180		CreateTestConfiguredJarList([]string{":jarA"})
181	})
182
183	list2 := list1.Append("apex2", "jarB")
184	t.Run("append", func(t *testing.T) {
185		assertStringEquals(t, "apex1:jarA,apex2:jarB", list2.String())
186	})
187
188	t.Run("append does not modify", func(t *testing.T) {
189		assertStringEquals(t, "apex1:jarA", list1.String())
190	})
191
192	// Make sure that two lists created by appending to the same list do not share storage.
193	list3 := list1.Append("apex3", "jarC")
194	t.Run("append does not share", func(t *testing.T) {
195		assertStringEquals(t, "apex1:jarA,apex2:jarB", list2.String())
196		assertStringEquals(t, "apex1:jarA,apex3:jarC", list3.String())
197	})
198
199	list4 := list3.RemoveList(list1)
200	t.Run("remove", func(t *testing.T) {
201		assertStringEquals(t, "apex3:jarC", list4.String())
202	})
203
204	t.Run("remove does not modify", func(t *testing.T) {
205		assertStringEquals(t, "apex1:jarA,apex3:jarC", list3.String())
206	})
207
208	// Make sure that two lists created by removing from the same list do not share storage.
209	list5 := list3.RemoveList(CreateTestConfiguredJarList([]string{"apex3:jarC"}))
210	t.Run("remove", func(t *testing.T) {
211		assertStringEquals(t, "apex3:jarC", list4.String())
212		assertStringEquals(t, "apex1:jarA", list5.String())
213	})
214}
215
216func (p partialCompileFlags) updateUseD8(value bool) partialCompileFlags {
217	p.Use_d8 = value
218	return p
219}
220
221func (p partialCompileFlags) updateDisableApiLint(value bool) partialCompileFlags {
222	p.Disable_api_lint = value
223	return p
224}
225
226func (p partialCompileFlags) updateDisableStubValidation(value bool) partialCompileFlags {
227	p.Disable_stub_validation = value
228	return p
229}
230
231func TestPartialCompile(t *testing.T) {
232	mockConfig := func(value string) *config {
233		c := &config{
234			env: map[string]string{
235				"SOONG_PARTIAL_COMPILE": value,
236			},
237		}
238		return c
239	}
240	tests := []struct {
241		value      string
242		isEngBuild bool
243		expected   partialCompileFlags
244	}{
245		{"", true, defaultPartialCompileFlags},
246		{"false", true, partialCompileFlags{}},
247		{"true", true, enabledPartialCompileFlags},
248		{"true", false, partialCompileFlags{}},
249		{"all", true, partialCompileFlags{}.updateUseD8(true).updateDisableApiLint(true).updateDisableStubValidation(true)},
250
251		// This verifies both use_d8 and the processing order.
252		{"true,use_d8", true, enabledPartialCompileFlags.updateUseD8(true)},
253		{"true,-use_d8", true, enabledPartialCompileFlags.updateUseD8(false)},
254		{"use_d8,false", true, partialCompileFlags{}},
255		{"false,+use_d8", true, partialCompileFlags{}.updateUseD8(true)},
256
257		// disable_api_lint can be specified with any of 3 options.
258		{"false,-api_lint", true, partialCompileFlags{}.updateDisableApiLint(true)},
259		{"false,-enable_api_lint", true, partialCompileFlags{}.updateDisableApiLint(true)},
260		{"false,+disable_api_lint", true, partialCompileFlags{}.updateDisableApiLint(true)},
261		{"false,+api_lint", true, partialCompileFlags{}.updateDisableApiLint(false)},
262		{"false,+enable_api_lint", true, partialCompileFlags{}.updateDisableApiLint(false)},
263		{"false,-disable_api_lint", true, partialCompileFlags{}.updateDisableApiLint(false)},
264
265		// disable_stub_validation can be specified with any of 3 options.
266		{"false,-stub_validation", true, partialCompileFlags{}.updateDisableStubValidation(true)},
267		{"false,-enable_stub_validation", true, partialCompileFlags{}.updateDisableStubValidation(true)},
268		{"false,+disable_stub_validation", true, partialCompileFlags{}.updateDisableStubValidation(true)},
269		{"false,+stub_validation", true, partialCompileFlags{}.updateDisableStubValidation(false)},
270		{"false,+enable_stub_validation", true, partialCompileFlags{}.updateDisableStubValidation(false)},
271		{"false,-disable_stub_validation", true, partialCompileFlags{}.updateDisableStubValidation(false)},
272	}
273
274	for _, test := range tests {
275		t.Run(test.value, func(t *testing.T) {
276			config := mockConfig(test.value)
277			flags, _ := config.parsePartialCompileFlags(test.isEngBuild)
278			if flags != test.expected {
279				t.Errorf("expected %v found %v", test.expected, flags)
280			}
281		})
282	}
283}
284
285type configTestProperties struct {
286	Use_generic_config *bool
287}
288
289type configTestModule struct {
290	ModuleBase
291	properties configTestProperties
292}
293
294func (d *configTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
295	deviceName := ctx.Config().DeviceName()
296	if ctx.ModuleName() == "foo" {
297		if ctx.Module().UseGenericConfig() {
298			ctx.PropertyErrorf("use_generic_config", "must not be set for this test")
299		}
300	} else if ctx.ModuleName() == "bar" {
301		if !ctx.Module().UseGenericConfig() {
302			ctx.ModuleErrorf("\"use_generic_config: true\" must be set for this test")
303		}
304	}
305
306	if ctx.Module().UseGenericConfig() {
307		if deviceName != "generic" {
308			ctx.ModuleErrorf("Device name for this module must be \"generic\" but %q\n", deviceName)
309		}
310	} else {
311		if deviceName == "generic" {
312			ctx.ModuleErrorf("Device name for this module must not be \"generic\"\n")
313		}
314	}
315}
316
317func configTestModuleFactory() Module {
318	module := &configTestModule{}
319	module.AddProperties(&module.properties)
320	InitAndroidModule(module)
321	return module
322}
323
324var prepareForConfigTest = GroupFixturePreparers(
325	FixtureRegisterWithContext(func(ctx RegistrationContext) {
326		ctx.RegisterModuleType("test", configTestModuleFactory)
327	}),
328)
329
330func TestGenericConfig(t *testing.T) {
331	bp := `
332		test {
333			name: "foo",
334		}
335
336		test {
337			name: "bar",
338			use_generic_config: true,
339		}
340	`
341
342	result := GroupFixturePreparers(
343		prepareForConfigTest,
344		FixtureWithRootAndroidBp(bp),
345	).RunTest(t)
346
347	foo := result.Module("foo", "").(*configTestModule)
348	bar := result.Module("bar", "").(*configTestModule)
349
350	AssertBoolEquals(t, "Do not use generic config", false, foo.UseGenericConfig())
351	AssertBoolEquals(t, "Use generic config", true, bar.UseGenericConfig())
352}
353