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 outputPath ModuleOutPath 39} 40 41type soongConfigTestModuleProperties struct { 42 Cflags []string 43} 44 45func soongConfigTestModuleFactory() Module { 46 m := &soongConfigTestModule{} 47 m.AddProperties(&m.props) 48 InitAndroidModule(m) 49 InitDefaultableModule(m) 50 return m 51} 52 53func (t *soongConfigTestModule) GenerateAndroidBuildActions(ctx ModuleContext) { 54 t.outputPath = PathForModuleOut(ctx, "test") 55} 56 57var prepareForSoongConfigTestModule = FixtureRegisterWithContext(func(ctx RegistrationContext) { 58 ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory) 59 ctx.RegisterModuleType("test", soongConfigTestModuleFactory) 60}) 61 62func TestSoongConfigModule(t *testing.T) { 63 configBp := ` 64 soong_config_module_type { 65 name: "acme_test", 66 module_type: "test", 67 config_namespace: "acme", 68 variables: ["board", "feature1", "FEATURE3", "unused_string_var"], 69 bool_variables: ["feature2", "unused_feature", "always_true"], 70 value_variables: ["size", "unused_size"], 71 properties: ["cflags", "srcs", "defaults"], 72 } 73 74 soong_config_string_variable { 75 name: "board", 76 values: ["soc_a", "soc_b", "soc_c", "soc_d"], 77 } 78 79 soong_config_string_variable { 80 name: "unused_string_var", 81 values: ["a", "b"], 82 } 83 84 soong_config_bool_variable { 85 name: "feature1", 86 } 87 88 soong_config_bool_variable { 89 name: "FEATURE3", 90 } 91 ` 92 93 importBp := ` 94 soong_config_module_type_import { 95 from: "SoongConfig.bp", 96 module_types: ["acme_test"], 97 } 98 ` 99 100 bp := ` 101 test_defaults { 102 name: "foo_defaults", 103 cflags: ["DEFAULT"], 104 } 105 106 acme_test { 107 name: "foo", 108 cflags: ["-DGENERIC"], 109 defaults: ["foo_defaults"], 110 soong_config_variables: { 111 board: { 112 soc_a: { 113 cflags: ["-DSOC_A"], 114 }, 115 soc_b: { 116 cflags: ["-DSOC_B"], 117 }, 118 soc_c: {}, 119 conditions_default: { 120 cflags: ["-DSOC_CONDITIONS_DEFAULT"], 121 }, 122 }, 123 size: { 124 cflags: ["-DSIZE=%s"], 125 conditions_default: { 126 cflags: ["-DSIZE=CONDITIONS_DEFAULT"], 127 }, 128 }, 129 feature1: { 130 conditions_default: { 131 cflags: ["-DF1_CONDITIONS_DEFAULT"], 132 }, 133 cflags: ["-DFEATURE1"], 134 }, 135 feature2: { 136 cflags: ["-DFEATURE2"], 137 conditions_default: { 138 cflags: ["-DF2_CONDITIONS_DEFAULT"], 139 }, 140 }, 141 FEATURE3: { 142 cflags: ["-DFEATURE3"], 143 }, 144 }, 145 } 146 147 test_defaults { 148 name: "foo_defaults_a", 149 cflags: ["DEFAULT_A"], 150 } 151 152 test_defaults { 153 name: "foo_defaults_b", 154 cflags: ["DEFAULT_B"], 155 } 156 157 test_defaults { 158 name: "foo_defaults_always_true", 159 cflags: ["DEFAULT_ALWAYS_TRUE"], 160 } 161 162 acme_test { 163 name: "foo_with_defaults", 164 cflags: ["-DGENERIC"], 165 defaults: ["foo_defaults"], 166 soong_config_variables: { 167 board: { 168 soc_a: { 169 cflags: ["-DSOC_A"], 170 defaults: ["foo_defaults_a"], 171 }, 172 soc_b: { 173 cflags: ["-DSOC_B"], 174 defaults: ["foo_defaults_b"], 175 }, 176 soc_c: {}, 177 }, 178 size: { 179 cflags: ["-DSIZE=%s"], 180 }, 181 feature1: { 182 cflags: ["-DFEATURE1"], 183 }, 184 feature2: { 185 cflags: ["-DFEATURE2"], 186 }, 187 FEATURE3: { 188 cflags: ["-DFEATURE3"], 189 }, 190 always_true: { 191 defaults: ["foo_defaults_always_true"], 192 conditions_default: { 193 // verify that conditions_default is skipped if the 194 // soong config variable is true by specifying a 195 // non-existent module in conditions_default 196 defaults: ["//nonexistent:defaults"], 197 } 198 }, 199 }, 200 } 201 ` 202 203 fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer { 204 return FixtureModifyProductVariables(func(variables FixtureProductVariables) { 205 variables.VendorVars = vars 206 }) 207 } 208 209 run := func(t *testing.T, bp string, fs MockFS) { 210 testCases := []struct { 211 name string 212 preparer FixturePreparer 213 fooExpectedFlags []string 214 fooDefaultsExpectedFlags []string 215 }{ 216 { 217 name: "withValues", 218 preparer: fixtureForVendorVars(map[string]map[string]string{ 219 "acme": { 220 "board": "soc_a", 221 "size": "42", 222 "feature1": "true", 223 "feature2": "false", 224 // FEATURE3 unset 225 "unused_feature": "true", // unused 226 "unused_size": "1", // unused 227 "unused_string_var": "a", // unused 228 "always_true": "true", 229 }, 230 }), 231 fooExpectedFlags: []string{ 232 "DEFAULT", 233 "-DGENERIC", 234 "-DF2_CONDITIONS_DEFAULT", 235 "-DSIZE=42", 236 "-DSOC_A", 237 "-DFEATURE1", 238 }, 239 fooDefaultsExpectedFlags: []string{ 240 "DEFAULT_A", 241 "DEFAULT_ALWAYS_TRUE", 242 "DEFAULT", 243 "-DGENERIC", 244 "-DSIZE=42", 245 "-DSOC_A", 246 "-DFEATURE1", 247 }, 248 }, 249 { 250 name: "empty_prop_for_string_var", 251 preparer: fixtureForVendorVars(map[string]map[string]string{ 252 "acme": { 253 "board": "soc_c", 254 "always_true": "true", 255 }}), 256 fooExpectedFlags: []string{ 257 "DEFAULT", 258 "-DGENERIC", 259 "-DF2_CONDITIONS_DEFAULT", 260 "-DSIZE=CONDITIONS_DEFAULT", 261 "-DF1_CONDITIONS_DEFAULT", 262 }, 263 fooDefaultsExpectedFlags: []string{ 264 "DEFAULT_ALWAYS_TRUE", 265 "DEFAULT", 266 "-DGENERIC", 267 }, 268 }, 269 { 270 name: "unused_string_var", 271 preparer: fixtureForVendorVars(map[string]map[string]string{ 272 "acme": { 273 "board": "soc_d", 274 "always_true": "true", 275 }}), 276 fooExpectedFlags: []string{ 277 "DEFAULT", 278 "-DGENERIC", 279 "-DF2_CONDITIONS_DEFAULT", 280 "-DSIZE=CONDITIONS_DEFAULT", 281 "-DSOC_CONDITIONS_DEFAULT", // foo does not contain a prop "soc_d", so we use the default 282 "-DF1_CONDITIONS_DEFAULT", 283 }, 284 fooDefaultsExpectedFlags: []string{ 285 "DEFAULT_ALWAYS_TRUE", 286 "DEFAULT", 287 "-DGENERIC", 288 }, 289 }, 290 291 { 292 name: "conditions_default", 293 preparer: fixtureForVendorVars(map[string]map[string]string{ 294 "acme": { 295 "always_true": "true", 296 }}), 297 fooExpectedFlags: []string{ 298 "DEFAULT", 299 "-DGENERIC", 300 "-DF2_CONDITIONS_DEFAULT", 301 "-DSIZE=CONDITIONS_DEFAULT", 302 "-DSOC_CONDITIONS_DEFAULT", 303 "-DF1_CONDITIONS_DEFAULT", 304 }, 305 fooDefaultsExpectedFlags: []string{ 306 "DEFAULT_ALWAYS_TRUE", 307 "DEFAULT", 308 "-DGENERIC", 309 }, 310 }, 311 } 312 313 for _, tc := range testCases { 314 t.Run(tc.name, func(t *testing.T) { 315 result := GroupFixturePreparers( 316 tc.preparer, 317 PrepareForTestWithDefaults, 318 PrepareForTestWithSoongConfigModuleBuildComponents, 319 prepareForSoongConfigTestModule, 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 PrepareForTestWithSoongConfigModuleBuildComponents, 375 prepareForSoongConfigTestModule, 376 FixtureWithRootAndroidBp(bp), 377 ).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{ 378 `unrecognized property "soong_config_variables.feature1.made_up_property`, 379 })).RunTest(t) 380} 381 382func TestDuplicateStringValueInSoongConfigStringVariable(t *testing.T) { 383 bp := ` 384 soong_config_string_variable { 385 name: "board", 386 values: ["soc_a", "soc_b", "soc_c", "soc_a"], 387 } 388 389 soong_config_module_type { 390 name: "acme_test", 391 module_type: "test", 392 config_namespace: "acme", 393 variables: ["board"], 394 properties: ["cflags", "srcs", "defaults"], 395 } 396 ` 397 398 fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer { 399 return FixtureModifyProductVariables(func(variables FixtureProductVariables) { 400 variables.VendorVars = vars 401 }) 402 } 403 404 GroupFixturePreparers( 405 fixtureForVendorVars(map[string]map[string]string{"acme": {"feature1": "1"}}), 406 PrepareForTestWithDefaults, 407 PrepareForTestWithSoongConfigModuleBuildComponents, 408 prepareForSoongConfigTestModule, 409 FixtureWithRootAndroidBp(bp), 410 ).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{ 411 // TODO(b/171232169): improve the error message for non-existent properties 412 `Android.bp: soong_config_string_variable: values property error: duplicate value: "soc_a"`, 413 })).RunTest(t) 414} 415 416type soongConfigTestSingletonModule struct { 417 SingletonModuleBase 418 props soongConfigTestSingletonModuleProperties 419} 420 421type soongConfigTestSingletonModuleProperties struct { 422 Fragments []struct { 423 Apex string 424 Module string 425 } 426} 427 428func soongConfigTestSingletonModuleFactory() SingletonModule { 429 m := &soongConfigTestSingletonModule{} 430 m.AddProperties(&m.props) 431 InitAndroidModule(m) 432 return m 433} 434 435func (t *soongConfigTestSingletonModule) GenerateAndroidBuildActions(ModuleContext) {} 436 437func (t *soongConfigTestSingletonModule) GenerateSingletonBuildActions(SingletonContext) {} 438 439var prepareForSoongConfigTestSingletonModule = FixtureRegisterWithContext(func(ctx RegistrationContext) { 440 ctx.RegisterSingletonModuleType("test_singleton", soongConfigTestSingletonModuleFactory) 441}) 442 443func TestSoongConfigModuleSingletonModule(t *testing.T) { 444 bp := ` 445 soong_config_module_type { 446 name: "acme_test_singleton", 447 module_type: "test_singleton", 448 config_namespace: "acme", 449 bool_variables: ["coyote"], 450 properties: ["fragments"], 451 } 452 453 acme_test_singleton { 454 name: "wiley", 455 fragments: [ 456 { 457 apex: "com.android.acme", 458 module: "road-runner", 459 }, 460 ], 461 soong_config_variables: { 462 coyote: { 463 fragments: [ 464 { 465 apex: "com.android.acme", 466 module: "wiley", 467 }, 468 ], 469 }, 470 }, 471 } 472 ` 473 474 for _, test := range []struct { 475 coyote bool 476 expectedFragments string 477 }{ 478 { 479 coyote: false, 480 expectedFragments: "[{Apex:com.android.acme Module:road-runner}]", 481 }, 482 { 483 coyote: true, 484 expectedFragments: "[{Apex:com.android.acme Module:road-runner} {Apex:com.android.acme Module:wiley}]", 485 }, 486 } { 487 t.Run(fmt.Sprintf("coyote:%t", test.coyote), func(t *testing.T) { 488 result := GroupFixturePreparers( 489 PrepareForTestWithSoongConfigModuleBuildComponents, 490 prepareForSoongConfigTestSingletonModule, 491 FixtureWithRootAndroidBp(bp), 492 FixtureModifyProductVariables(func(variables FixtureProductVariables) { 493 variables.VendorVars = map[string]map[string]string{ 494 "acme": { 495 "coyote": fmt.Sprintf("%t", test.coyote), 496 }, 497 } 498 }), 499 ).RunTest(t) 500 501 // Make sure that the singleton was created. 502 result.SingletonForTests("test_singleton") 503 m := result.ModuleForTests("wiley", "").module.(*soongConfigTestSingletonModule) 504 AssertStringEquals(t, "fragments", test.expectedFragments, fmt.Sprintf("%+v", m.props.Fragments)) 505 }) 506 } 507} 508