xref: /aosp_15_r20/build/soong/android/config_test.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
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.Errorf(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) updateEnabled(value bool) partialCompileFlags {
217	p.enabled = value
218	return p
219}
220
221func (p partialCompileFlags) updateUseD8(value bool) partialCompileFlags {
222	p.use_d8 = value
223	return p
224}
225
226func TestPartialCompile(t *testing.T) {
227	mockConfig := func(value string) *config {
228		c := &config{
229			env: map[string]string{
230				"SOONG_PARTIAL_COMPILE": value,
231			},
232		}
233		return c
234	}
235	tests := []struct {
236		value      string
237		isEngBuild bool
238		expected   partialCompileFlags
239	}{
240		{"", true, defaultPartialCompileFlags},
241		{"false", true, partialCompileFlags{}},
242		{"true", true, defaultPartialCompileFlags.updateEnabled(true)},
243		{"true", false, partialCompileFlags{}},
244		{"true,use_d8", true, defaultPartialCompileFlags.updateEnabled(true).updateUseD8(true)},
245		{"true,-use_d8", true, defaultPartialCompileFlags.updateEnabled(true).updateUseD8(false)},
246		{"use_d8,false", true, partialCompileFlags{}},
247		{"false,+use_d8", true, partialCompileFlags{}.updateUseD8(true)},
248	}
249
250	for _, test := range tests {
251		t.Run(test.value, func(t *testing.T) {
252			config := mockConfig(test.value)
253			flags, _ := config.parsePartialCompileFlags(test.isEngBuild)
254			if flags != test.expected {
255				t.Errorf("expected %v found %v", test.expected, flags)
256			}
257		})
258	}
259}
260