Colin Cross | 7e0eaf1 | 2017-05-05 16:16:24 -0700 | [diff] [blame] | 1 | // Copyright 2015 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 | |
| 15 | package android |
| 16 | |
| 17 | import ( |
| 18 | "reflect" |
Colin Cross | 18c4680 | 2019-09-24 22:19:02 -0700 | [diff] [blame] | 19 | "strconv" |
Colin Cross | 7e0eaf1 | 2017-05-05 16:16:24 -0700 | [diff] [blame] | 20 | "testing" |
Colin Cross | 18c4680 | 2019-09-24 22:19:02 -0700 | [diff] [blame] | 21 | |
| 22 | "github.com/google/blueprint/proptools" |
Colin Cross | 7e0eaf1 | 2017-05-05 16:16:24 -0700 | [diff] [blame] | 23 | ) |
| 24 | |
| 25 | type printfIntoPropertyTestCase struct { |
| 26 | in string |
| 27 | val interface{} |
| 28 | out string |
| 29 | err bool |
| 30 | } |
| 31 | |
| 32 | var printfIntoPropertyTestCases = []printfIntoPropertyTestCase{ |
| 33 | { |
| 34 | in: "%d", |
| 35 | val: 0, |
| 36 | out: "0", |
| 37 | }, |
| 38 | { |
| 39 | in: "%d", |
| 40 | val: 1, |
| 41 | out: "1", |
| 42 | }, |
| 43 | { |
| 44 | in: "%d", |
| 45 | val: 2, |
| 46 | out: "2", |
| 47 | }, |
| 48 | { |
| 49 | in: "%d", |
| 50 | val: false, |
| 51 | out: "0", |
| 52 | }, |
| 53 | { |
| 54 | in: "%d", |
| 55 | val: true, |
| 56 | out: "1", |
| 57 | }, |
| 58 | { |
| 59 | in: "%d", |
| 60 | val: -1, |
| 61 | out: "-1", |
| 62 | }, |
| 63 | |
| 64 | { |
| 65 | in: "-DA=%d", |
| 66 | val: 1, |
| 67 | out: "-DA=1", |
| 68 | }, |
| 69 | { |
| 70 | in: "-DA=%du", |
| 71 | val: 1, |
| 72 | out: "-DA=1u", |
| 73 | }, |
| 74 | { |
| 75 | in: "-DA=%s", |
| 76 | val: "abc", |
| 77 | out: "-DA=abc", |
| 78 | }, |
| 79 | { |
| 80 | in: `-DA="%s"`, |
| 81 | val: "abc", |
| 82 | out: `-DA="abc"`, |
| 83 | }, |
| 84 | |
| 85 | { |
| 86 | in: "%%", |
| 87 | err: true, |
| 88 | }, |
| 89 | { |
| 90 | in: "%d%s", |
| 91 | err: true, |
| 92 | }, |
| 93 | { |
| 94 | in: "%d,%s", |
| 95 | err: true, |
| 96 | }, |
| 97 | { |
| 98 | in: "%d", |
| 99 | val: "", |
| 100 | err: true, |
| 101 | }, |
| 102 | { |
| 103 | in: "%d", |
| 104 | val: 1.5, |
| 105 | err: true, |
| 106 | }, |
| 107 | { |
| 108 | in: "%f", |
| 109 | val: 1.5, |
| 110 | err: true, |
| 111 | }, |
| 112 | } |
| 113 | |
| 114 | func TestPrintfIntoProperty(t *testing.T) { |
Colin Cross | 323dc60 | 2020-09-18 14:25:31 -0700 | [diff] [blame^] | 115 | t.Parallel() |
Colin Cross | 7e0eaf1 | 2017-05-05 16:16:24 -0700 | [diff] [blame] | 116 | for _, testCase := range printfIntoPropertyTestCases { |
| 117 | s := testCase.in |
| 118 | v := reflect.ValueOf(&s).Elem() |
| 119 | err := printfIntoProperty(v, testCase.val) |
| 120 | if err != nil && !testCase.err { |
| 121 | t.Errorf("unexpected error %s", err) |
| 122 | } else if err == nil && testCase.err { |
| 123 | t.Errorf("expected error") |
| 124 | } else if err == nil && v.String() != testCase.out { |
| 125 | t.Errorf("expected %q got %q", testCase.out, v.String()) |
| 126 | } |
| 127 | } |
| 128 | } |
Colin Cross | 18c4680 | 2019-09-24 22:19:02 -0700 | [diff] [blame] | 129 | |
| 130 | type testProductVariableModule struct { |
| 131 | ModuleBase |
| 132 | } |
| 133 | |
| 134 | func (m *testProductVariableModule) GenerateAndroidBuildActions(ctx ModuleContext) { |
| 135 | } |
| 136 | |
| 137 | var testProductVariableProperties = struct { |
| 138 | Product_variables struct { |
| 139 | Eng struct { |
| 140 | Srcs []string |
| 141 | Cflags []string |
| 142 | } |
| 143 | } |
| 144 | }{} |
| 145 | |
| 146 | func testProductVariableModuleFactoryFactory(props interface{}) func() Module { |
| 147 | return func() Module { |
| 148 | m := &testProductVariableModule{} |
| 149 | clonedProps := proptools.CloneProperties(reflect.ValueOf(props)).Interface() |
| 150 | m.AddProperties(clonedProps) |
| 151 | |
Colin Cross | 9d34f35 | 2019-11-22 16:03:51 -0800 | [diff] [blame] | 152 | // Set a default soongConfigVariableProperties, this will be used as the input to the property struct filter |
Colin Cross | 18c4680 | 2019-09-24 22:19:02 -0700 | [diff] [blame] | 153 | // for this test module. |
| 154 | m.variableProperties = testProductVariableProperties |
| 155 | InitAndroidModule(m) |
| 156 | return m |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | func TestProductVariables(t *testing.T) { |
Colin Cross | 323dc60 | 2020-09-18 14:25:31 -0700 | [diff] [blame^] | 161 | t.Parallel() |
Colin Cross | 18c4680 | 2019-09-24 22:19:02 -0700 | [diff] [blame] | 162 | ctx := NewTestContext() |
| 163 | // A module type that has a srcs property but not a cflags property. |
Colin Cross | 43e789d | 2020-01-28 09:46:50 -0800 | [diff] [blame] | 164 | ctx.RegisterModuleType("module1", testProductVariableModuleFactoryFactory(&struct { |
Colin Cross | 18c4680 | 2019-09-24 22:19:02 -0700 | [diff] [blame] | 165 | Srcs []string |
Colin Cross | 4b49b76 | 2019-11-22 15:25:03 -0800 | [diff] [blame] | 166 | }{})) |
Colin Cross | 18c4680 | 2019-09-24 22:19:02 -0700 | [diff] [blame] | 167 | // A module type that has a cflags property but not a srcs property. |
Colin Cross | 43e789d | 2020-01-28 09:46:50 -0800 | [diff] [blame] | 168 | ctx.RegisterModuleType("module2", testProductVariableModuleFactoryFactory(&struct { |
Colin Cross | 18c4680 | 2019-09-24 22:19:02 -0700 | [diff] [blame] | 169 | Cflags []string |
Colin Cross | 4b49b76 | 2019-11-22 15:25:03 -0800 | [diff] [blame] | 170 | }{})) |
Colin Cross | 18c4680 | 2019-09-24 22:19:02 -0700 | [diff] [blame] | 171 | // A module type that does not have any properties that match product_variables. |
Colin Cross | 43e789d | 2020-01-28 09:46:50 -0800 | [diff] [blame] | 172 | ctx.RegisterModuleType("module3", testProductVariableModuleFactoryFactory(&struct { |
Colin Cross | 18c4680 | 2019-09-24 22:19:02 -0700 | [diff] [blame] | 173 | Foo []string |
Colin Cross | 4b49b76 | 2019-11-22 15:25:03 -0800 | [diff] [blame] | 174 | }{})) |
Colin Cross | 18c4680 | 2019-09-24 22:19:02 -0700 | [diff] [blame] | 175 | ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) { |
Colin Cross | eabaedd | 2020-02-06 17:01:55 -0800 | [diff] [blame] | 176 | ctx.BottomUp("variable", VariableMutator).Parallel() |
Colin Cross | 18c4680 | 2019-09-24 22:19:02 -0700 | [diff] [blame] | 177 | }) |
| 178 | |
| 179 | // Test that a module can use one product variable even if it doesn't have all the properties |
| 180 | // supported by that product variable. |
| 181 | bp := ` |
| 182 | module1 { |
| 183 | name: "foo", |
| 184 | product_variables: { |
| 185 | eng: { |
| 186 | srcs: ["foo.c"], |
| 187 | }, |
| 188 | }, |
| 189 | } |
| 190 | module2 { |
| 191 | name: "bar", |
| 192 | product_variables: { |
| 193 | eng: { |
| 194 | cflags: ["-DBAR"], |
| 195 | }, |
| 196 | }, |
| 197 | } |
| 198 | |
| 199 | module3 { |
| 200 | name: "baz", |
| 201 | } |
| 202 | ` |
Colin Cross | 98be1bb | 2019-12-13 20:41:13 -0800 | [diff] [blame] | 203 | config := TestConfig(buildDir, nil, bp, nil) |
Colin Cross | 18c4680 | 2019-09-24 22:19:02 -0700 | [diff] [blame] | 204 | config.TestProductVariables.Eng = proptools.BoolPtr(true) |
| 205 | |
Colin Cross | 98be1bb | 2019-12-13 20:41:13 -0800 | [diff] [blame] | 206 | ctx.Register(config) |
| 207 | |
Colin Cross | 18c4680 | 2019-09-24 22:19:02 -0700 | [diff] [blame] | 208 | _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) |
| 209 | FailIfErrored(t, errs) |
| 210 | _, errs = ctx.PrepareBuildActions(config) |
| 211 | FailIfErrored(t, errs) |
| 212 | } |
| 213 | |
Colin Cross | eabaedd | 2020-02-06 17:01:55 -0800 | [diff] [blame] | 214 | var testProductVariableDefaultsProperties = struct { |
| 215 | Product_variables struct { |
| 216 | Eng struct { |
| 217 | Foo []string |
| 218 | Bar []string |
| 219 | } |
| 220 | } |
| 221 | }{} |
| 222 | |
| 223 | type productVariablesDefaultsTestProperties struct { |
| 224 | Foo []string |
| 225 | } |
| 226 | |
| 227 | type productVariablesDefaultsTestProperties2 struct { |
| 228 | Foo []string |
| 229 | Bar []string |
| 230 | } |
| 231 | |
| 232 | type productVariablesDefaultsTestModule struct { |
| 233 | ModuleBase |
| 234 | DefaultableModuleBase |
| 235 | properties productVariablesDefaultsTestProperties |
| 236 | } |
| 237 | |
| 238 | func (d *productVariablesDefaultsTestModule) GenerateAndroidBuildActions(ctx ModuleContext) { |
| 239 | ctx.Build(pctx, BuildParams{ |
| 240 | Rule: Touch, |
| 241 | Output: PathForModuleOut(ctx, "out"), |
| 242 | }) |
| 243 | } |
| 244 | |
| 245 | func productVariablesDefaultsTestModuleFactory() Module { |
| 246 | module := &productVariablesDefaultsTestModule{} |
| 247 | module.AddProperties(&module.properties) |
| 248 | module.variableProperties = testProductVariableDefaultsProperties |
| 249 | InitAndroidModule(module) |
| 250 | InitDefaultableModule(module) |
| 251 | return module |
| 252 | } |
| 253 | |
| 254 | type productVariablesDefaultsTestDefaults struct { |
| 255 | ModuleBase |
| 256 | DefaultsModuleBase |
| 257 | } |
| 258 | |
| 259 | func productVariablesDefaultsTestDefaultsFactory() Module { |
| 260 | defaults := &productVariablesDefaultsTestDefaults{} |
| 261 | defaults.AddProperties(&productVariablesDefaultsTestProperties{}) |
| 262 | defaults.AddProperties(&productVariablesDefaultsTestProperties2{}) |
| 263 | defaults.variableProperties = testProductVariableDefaultsProperties |
| 264 | InitDefaultsModule(defaults) |
| 265 | return defaults |
| 266 | } |
| 267 | |
| 268 | // Test a defaults module that supports more product variable properties than the target module. |
| 269 | func TestProductVariablesDefaults(t *testing.T) { |
Colin Cross | 323dc60 | 2020-09-18 14:25:31 -0700 | [diff] [blame^] | 270 | t.Parallel() |
Colin Cross | eabaedd | 2020-02-06 17:01:55 -0800 | [diff] [blame] | 271 | bp := ` |
| 272 | defaults { |
| 273 | name: "defaults", |
| 274 | product_variables: { |
| 275 | eng: { |
| 276 | foo: ["product_variable_defaults"], |
| 277 | bar: ["product_variable_defaults"], |
| 278 | }, |
| 279 | }, |
| 280 | foo: ["defaults"], |
| 281 | bar: ["defaults"], |
| 282 | } |
| 283 | |
| 284 | test { |
| 285 | name: "foo", |
| 286 | defaults: ["defaults"], |
| 287 | foo: ["module"], |
| 288 | product_variables: { |
| 289 | eng: { |
| 290 | foo: ["product_variable_module"], |
| 291 | }, |
| 292 | }, |
| 293 | } |
| 294 | ` |
| 295 | |
| 296 | config := TestConfig(buildDir, nil, bp, nil) |
| 297 | config.TestProductVariables.Eng = boolPtr(true) |
| 298 | |
| 299 | ctx := NewTestContext() |
| 300 | |
| 301 | ctx.RegisterModuleType("test", productVariablesDefaultsTestModuleFactory) |
| 302 | ctx.RegisterModuleType("defaults", productVariablesDefaultsTestDefaultsFactory) |
| 303 | |
| 304 | ctx.PreArchMutators(RegisterDefaultsPreArchMutators) |
| 305 | ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) { |
| 306 | ctx.BottomUp("variable", VariableMutator).Parallel() |
| 307 | }) |
| 308 | |
| 309 | ctx.Register(config) |
| 310 | |
| 311 | _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) |
| 312 | FailIfErrored(t, errs) |
| 313 | _, errs = ctx.PrepareBuildActions(config) |
| 314 | FailIfErrored(t, errs) |
| 315 | |
| 316 | foo := ctx.ModuleForTests("foo", "").Module().(*productVariablesDefaultsTestModule) |
| 317 | |
| 318 | want := []string{"defaults", "module", "product_variable_defaults", "product_variable_module"} |
| 319 | if g, w := foo.properties.Foo, want; !reflect.DeepEqual(g, w) { |
| 320 | t.Errorf("expected foo %q, got %q", w, g) |
| 321 | } |
| 322 | } |
| 323 | |
Colin Cross | 18c4680 | 2019-09-24 22:19:02 -0700 | [diff] [blame] | 324 | func BenchmarkSliceToTypeArray(b *testing.B) { |
| 325 | for _, n := range []int{1, 2, 4, 8, 100} { |
| 326 | var propStructs []interface{} |
| 327 | for i := 0; i < n; i++ { |
| 328 | propStructs = append(propStructs, &struct { |
| 329 | A *string |
| 330 | B string |
| 331 | }{}) |
| 332 | |
| 333 | } |
| 334 | b.Run(strconv.Itoa(n), func(b *testing.B) { |
| 335 | for i := 0; i < b.N; i++ { |
| 336 | _ = sliceToTypeArray(propStructs) |
| 337 | } |
| 338 | }) |
| 339 | } |
| 340 | } |