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