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