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