blob: 1eb137beb2f27e45a3016bd0e131165e928376bd [file] [log] [blame]
Cole Faust5a231bd2024-02-07 09:43:59 -08001// Copyright 2024 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 "reflect"
20 "testing"
21
22 "github.com/google/blueprint"
23 "github.com/google/blueprint/proptools"
24)
25
26func TestSelects(t *testing.T) {
27 testCases := []struct {
28 name string
29 bp string
30 provider selectsTestProvider
31 vendorVars map[string]map[string]string
32 expectedError string
33 }{
34 {
35 name: "basic string list",
36 bp: `
37 my_module_type {
38 name: "foo",
39 my_string_list: select(soong_config_variable("my_namespace", "my_variable"), {
40 "a": ["a.cpp"],
41 "b": ["b.cpp"],
Cole Faust683316a2024-04-02 16:45:54 -070042 default: ["c.cpp"],
Cole Faust5a231bd2024-02-07 09:43:59 -080043 }),
44 }
45 `,
46 provider: selectsTestProvider{
47 my_string_list: &[]string{"c.cpp"},
48 },
49 },
50 {
51 name: "basic string",
52 bp: `
53 my_module_type {
54 name: "foo",
55 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
56 "a": "a.cpp",
57 "b": "b.cpp",
Cole Faust683316a2024-04-02 16:45:54 -070058 default: "c.cpp",
Cole Faust5a231bd2024-02-07 09:43:59 -080059 }),
60 }
61 `,
62 provider: selectsTestProvider{
63 my_string: proptools.StringPtr("c.cpp"),
64 },
65 },
66 {
67 name: "basic bool",
68 bp: `
69 my_module_type {
70 name: "foo",
71 my_bool: select(soong_config_variable("my_namespace", "my_variable"), {
72 "a": true,
73 "b": false,
Cole Faust683316a2024-04-02 16:45:54 -070074 default: true,
Cole Faust5a231bd2024-02-07 09:43:59 -080075 }),
76 }
77 `,
78 provider: selectsTestProvider{
79 my_bool: proptools.BoolPtr(true),
80 },
81 },
82 {
Cole Faustbdd8aee2024-03-14 14:33:02 -070083 name: "basic paths",
84 bp: `
85 my_module_type {
86 name: "foo",
87 my_paths: select(soong_config_variable("my_namespace", "my_variable"), {
88 "a": ["foo.txt"],
89 "b": ["bar.txt"],
Cole Faust683316a2024-04-02 16:45:54 -070090 default: ["baz.txt"],
Cole Faustbdd8aee2024-03-14 14:33:02 -070091 }),
92 }
93 `,
94 provider: selectsTestProvider{
95 my_paths: &[]string{"baz.txt"},
96 },
97 },
98 {
99 name: "paths with module references",
100 bp: `
101 my_module_type {
102 name: "foo",
103 my_paths: select(soong_config_variable("my_namespace", "my_variable"), {
104 "a": [":a"],
105 "b": [":b"],
Cole Faust683316a2024-04-02 16:45:54 -0700106 default: [":c"],
Cole Faustbdd8aee2024-03-14 14:33:02 -0700107 }),
108 }
109 `,
110 expectedError: `"foo" depends on undefined module "c"`,
111 },
112 {
Cole Faust5a231bd2024-02-07 09:43:59 -0800113 name: "Differing types",
114 bp: `
115 my_module_type {
116 name: "foo",
117 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
118 "a": "a.cpp",
119 "b": true,
Cole Faust683316a2024-04-02 16:45:54 -0700120 default: "c.cpp",
Cole Faust5a231bd2024-02-07 09:43:59 -0800121 }),
122 }
123 `,
Cole Faust12c8ed42024-03-28 16:26:59 -0700124 expectedError: `Android.bp:8:5: Found select statement with differing types "string" and "bool" in its cases`,
125 },
126 {
127 name: "Select type doesn't match property type",
128 bp: `
129 my_module_type {
130 name: "foo",
131 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
132 "a": false,
133 "b": true,
Cole Faust683316a2024-04-02 16:45:54 -0700134 default: true,
Cole Faust12c8ed42024-03-28 16:26:59 -0700135 }),
136 }
137 `,
138 expectedError: `can't assign bool value to string property "my_string\[0\]"`,
Cole Faust5a231bd2024-02-07 09:43:59 -0800139 },
140 {
141 name: "String list non-default",
142 bp: `
143 my_module_type {
144 name: "foo",
145 my_string_list: select(soong_config_variable("my_namespace", "my_variable"), {
146 "a": ["a.cpp"],
147 "b": ["b.cpp"],
Cole Faust683316a2024-04-02 16:45:54 -0700148 default: ["c.cpp"],
Cole Faust5a231bd2024-02-07 09:43:59 -0800149 }),
150 }
151 `,
152 provider: selectsTestProvider{
153 my_string_list: &[]string{"a.cpp"},
154 },
155 vendorVars: map[string]map[string]string{
156 "my_namespace": {
157 "my_variable": "a",
158 },
159 },
160 },
161 {
162 name: "String list append",
163 bp: `
164 my_module_type {
165 name: "foo",
166 my_string_list: select(soong_config_variable("my_namespace", "my_variable"), {
167 "a": ["a.cpp"],
168 "b": ["b.cpp"],
Cole Faust683316a2024-04-02 16:45:54 -0700169 default: ["c.cpp"],
Cole Faust5a231bd2024-02-07 09:43:59 -0800170 }) + select(soong_config_variable("my_namespace", "my_variable_2"), {
171 "a2": ["a2.cpp"],
172 "b2": ["b2.cpp"],
Cole Faust683316a2024-04-02 16:45:54 -0700173 default: ["c2.cpp"],
Cole Faust5a231bd2024-02-07 09:43:59 -0800174 }),
175 }
176 `,
177 provider: selectsTestProvider{
178 my_string_list: &[]string{"a.cpp", "c2.cpp"},
179 },
180 vendorVars: map[string]map[string]string{
181 "my_namespace": {
182 "my_variable": "a",
183 },
184 },
185 },
186 {
187 name: "String list prepend literal",
188 bp: `
189 my_module_type {
190 name: "foo",
191 my_string_list: ["literal.cpp"] + select(soong_config_variable("my_namespace", "my_variable"), {
192 "a2": ["a2.cpp"],
193 "b2": ["b2.cpp"],
Cole Faust683316a2024-04-02 16:45:54 -0700194 default: ["c2.cpp"],
Cole Faust5a231bd2024-02-07 09:43:59 -0800195 }),
196 }
197 `,
198 provider: selectsTestProvider{
199 my_string_list: &[]string{"literal.cpp", "c2.cpp"},
200 },
201 },
202 {
203 name: "String list append literal",
204 bp: `
205 my_module_type {
206 name: "foo",
207 my_string_list: select(soong_config_variable("my_namespace", "my_variable"), {
208 "a2": ["a2.cpp"],
209 "b2": ["b2.cpp"],
Cole Faust683316a2024-04-02 16:45:54 -0700210 default: ["c2.cpp"],
Cole Faust5a231bd2024-02-07 09:43:59 -0800211 }) + ["literal.cpp"],
212 }
213 `,
214 provider: selectsTestProvider{
215 my_string_list: &[]string{"c2.cpp", "literal.cpp"},
216 },
217 },
218 {
Cole Faust74ef4652024-03-27 16:45:41 -0700219 name: "true + false = true",
Cole Faust5a231bd2024-02-07 09:43:59 -0800220 bp: `
221 my_module_type {
222 name: "foo",
223 my_bool: select(soong_config_variable("my_namespace", "my_variable"), {
224 "a": true,
225 "b": false,
Cole Faust683316a2024-04-02 16:45:54 -0700226 default: true,
Cole Faust5a231bd2024-02-07 09:43:59 -0800227 }) + false,
228 }
229 `,
Cole Faust74ef4652024-03-27 16:45:41 -0700230 provider: selectsTestProvider{
231 my_bool: proptools.BoolPtr(true),
232 },
233 },
234 {
235 name: "false + false = false",
236 bp: `
237 my_module_type {
238 name: "foo",
239 my_bool: select(soong_config_variable("my_namespace", "my_variable"), {
240 "a": true,
241 "b": false,
Cole Faust683316a2024-04-02 16:45:54 -0700242 default: true,
Cole Faust74ef4652024-03-27 16:45:41 -0700243 }) + false,
244 }
245 `,
246 vendorVars: map[string]map[string]string{
247 "my_namespace": {
248 "my_variable": "b",
249 },
250 },
251 provider: selectsTestProvider{
252 my_bool: proptools.BoolPtr(false),
253 },
Cole Faust5a231bd2024-02-07 09:43:59 -0800254 },
255 {
256 name: "Append string",
257 bp: `
258 my_module_type {
259 name: "foo",
260 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
261 "a": "a",
262 "b": "b",
Cole Faust683316a2024-04-02 16:45:54 -0700263 default: "c",
Cole Faust5a231bd2024-02-07 09:43:59 -0800264 }) + ".cpp",
265 }
266 `,
267 provider: selectsTestProvider{
268 my_string: proptools.StringPtr("c.cpp"),
269 },
270 },
Cole Faust0aa21cc2024-03-20 12:28:03 -0700271 {
272 name: "Select on variant",
273 bp: `
274 my_module_type {
275 name: "foo",
276 my_string: select(variant("arch"), {
277 "x86": "my_x86",
278 "x86_64": "my_x86_64",
279 "arm": "my_arm",
280 "arm64": "my_arm64",
Cole Faust683316a2024-04-02 16:45:54 -0700281 default: "my_default",
Cole Faust0aa21cc2024-03-20 12:28:03 -0700282 }),
283 }
284 `,
285 provider: selectsTestProvider{
286 my_string: proptools.StringPtr("my_arm64"),
287 },
288 },
Cole Faust12c8ed42024-03-28 16:26:59 -0700289 {
290 name: "Unset value",
291 bp: `
292 my_module_type {
293 name: "foo",
294 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
295 "a": unset,
296 "b": "b",
Cole Faust683316a2024-04-02 16:45:54 -0700297 default: "c",
Cole Faust12c8ed42024-03-28 16:26:59 -0700298 })
299 }
300 `,
301 vendorVars: map[string]map[string]string{
302 "my_namespace": {
303 "my_variable": "a",
304 },
305 },
306 provider: selectsTestProvider{},
307 },
308 {
309 name: "Unset value on different branch",
310 bp: `
311 my_module_type {
312 name: "foo",
313 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
314 "a": unset,
315 "b": "b",
Cole Faust683316a2024-04-02 16:45:54 -0700316 default: "c",
Cole Faust12c8ed42024-03-28 16:26:59 -0700317 })
318 }
319 `,
320 provider: selectsTestProvider{
321 my_string: proptools.StringPtr("c"),
322 },
323 },
324 {
325 name: "unset + unset = unset",
326 bp: `
327 my_module_type {
328 name: "foo",
329 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
Cole Faustfdbf5d42024-04-10 15:01:23 -0700330 "foo": "bar",
Cole Faust683316a2024-04-02 16:45:54 -0700331 default: unset,
Cole Faust12c8ed42024-03-28 16:26:59 -0700332 }) + select(soong_config_variable("my_namespace", "my_variable2"), {
Cole Faustfdbf5d42024-04-10 15:01:23 -0700333 "baz": "qux",
Cole Faust683316a2024-04-02 16:45:54 -0700334 default: unset,
Cole Faust12c8ed42024-03-28 16:26:59 -0700335 })
336 }
337 `,
338 provider: selectsTestProvider{},
339 },
340 {
341 name: "unset + string = string",
342 bp: `
343 my_module_type {
344 name: "foo",
345 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
Cole Faustfdbf5d42024-04-10 15:01:23 -0700346 "foo": "bar",
Cole Faust683316a2024-04-02 16:45:54 -0700347 default: unset,
Cole Faust12c8ed42024-03-28 16:26:59 -0700348 }) + select(soong_config_variable("my_namespace", "my_variable2"), {
Cole Faust683316a2024-04-02 16:45:54 -0700349 default: "a",
Cole Faust12c8ed42024-03-28 16:26:59 -0700350 })
351 }
352 `,
353 provider: selectsTestProvider{
354 my_string: proptools.StringPtr("a"),
355 },
356 },
357 {
358 name: "unset + bool = bool",
359 bp: `
360 my_module_type {
361 name: "foo",
362 my_bool: select(soong_config_variable("my_namespace", "my_variable"), {
363 "a": true,
Cole Faust683316a2024-04-02 16:45:54 -0700364 default: unset,
Cole Faust12c8ed42024-03-28 16:26:59 -0700365 }) + select(soong_config_variable("my_namespace", "my_variable2"), {
Cole Faust683316a2024-04-02 16:45:54 -0700366 default: true,
Cole Faust12c8ed42024-03-28 16:26:59 -0700367 })
368 }
369 `,
370 provider: selectsTestProvider{
371 my_bool: proptools.BoolPtr(true),
372 },
373 },
Cole Faust02dd6e52024-04-03 17:04:57 -0700374 {
375 name: "defaults with lists are appended",
376 bp: `
377 my_module_type {
378 name: "foo",
379 defaults: ["bar"],
380 my_string_list: select(soong_config_variable("my_namespace", "my_variable"), {
381 "a": ["a1"],
382 default: ["b1"],
383 }),
384 }
385 my_defaults {
386 name: "bar",
387 my_string_list: select(soong_config_variable("my_namespace", "my_variable2"), {
388 "a": ["a2"],
389 default: ["b2"],
390 }),
391 }
392 `,
393 provider: selectsTestProvider{
394 my_string_list: &[]string{"b2", "b1"},
395 },
396 },
397 {
398 name: "Replacing string list",
399 bp: `
400 my_module_type {
401 name: "foo",
402 defaults: ["bar"],
403 replacing_string_list: select(soong_config_variable("my_namespace", "my_variable"), {
404 "a": ["a1"],
405 default: ["b1"],
406 }),
407 }
408 my_defaults {
409 name: "bar",
410 replacing_string_list: select(soong_config_variable("my_namespace", "my_variable2"), {
411 "a": ["a2"],
412 default: ["b2"],
413 }),
414 }
415 `,
416 provider: selectsTestProvider{
417 replacing_string_list: &[]string{"b1"},
418 },
419 },
Cole Faustfdbf5d42024-04-10 15:01:23 -0700420 {
421 name: "Multi-condition string 1",
422 bp: `
423 my_module_type {
424 name: "foo",
425 my_string: select((
426 soong_config_variable("my_namespace", "my_variable"),
427 soong_config_variable("my_namespace", "my_variable2"),
428 ), {
429 ("a", "b"): "a+b",
430 ("a", default): "a+default",
431 (default, default): "default",
432 }),
433 }
434 `,
435 vendorVars: map[string]map[string]string{
436 "my_namespace": {
437 "my_variable": "a",
438 "my_variable2": "b",
439 },
440 },
441 provider: selectsTestProvider{
442 my_string: proptools.StringPtr("a+b"),
443 },
444 },
445 {
446 name: "Multi-condition string 2",
447 bp: `
448 my_module_type {
449 name: "foo",
450 my_string: select((
451 soong_config_variable("my_namespace", "my_variable"),
452 soong_config_variable("my_namespace", "my_variable2"),
453 ), {
454 ("a", "b"): "a+b",
455 ("a", default): "a+default",
456 (default, default): "default",
457 }),
458 }
459 `,
460 vendorVars: map[string]map[string]string{
461 "my_namespace": {
462 "my_variable": "a",
463 "my_variable2": "c",
464 },
465 },
466 provider: selectsTestProvider{
467 my_string: proptools.StringPtr("a+default"),
468 },
469 },
470 {
471 name: "Multi-condition string 3",
472 bp: `
473 my_module_type {
474 name: "foo",
475 my_string: select((
476 soong_config_variable("my_namespace", "my_variable"),
477 soong_config_variable("my_namespace", "my_variable2"),
478 ), {
479 ("a", "b"): "a+b",
480 ("a", default): "a+default",
481 (default, default): "default",
482 }),
483 }
484 `,
485 vendorVars: map[string]map[string]string{
486 "my_namespace": {
487 "my_variable": "c",
488 "my_variable2": "b",
489 },
490 },
491 provider: selectsTestProvider{
492 my_string: proptools.StringPtr("default"),
493 },
494 },
495 {
496 name: "Select on boolean",
497 bp: `
498 my_module_type {
499 name: "foo",
500 my_string: select(boolean_var_for_testing(), {
501 true: "t",
502 false: "f",
503 }),
504 }
505 `,
506 vendorVars: map[string]map[string]string{
507 "boolean_var": {
508 "for_testing": "true",
509 },
510 },
511 provider: selectsTestProvider{
512 my_string: proptools.StringPtr("t"),
513 },
514 },
515 {
516 name: "Select on boolean false",
517 bp: `
518 my_module_type {
519 name: "foo",
520 my_string: select(boolean_var_for_testing(), {
521 true: "t",
522 false: "f",
523 }),
524 }
525 `,
526 vendorVars: map[string]map[string]string{
527 "boolean_var": {
528 "for_testing": "false",
529 },
530 },
531 provider: selectsTestProvider{
532 my_string: proptools.StringPtr("f"),
533 },
534 },
535 {
536 name: "Select on boolean undefined",
537 bp: `
538 my_module_type {
539 name: "foo",
540 my_string: select(boolean_var_for_testing(), {
541 true: "t",
542 false: "f",
543 }),
544 }
545 `,
546 expectedError: "foo",
547 },
548 {
549 name: "Select on boolean undefined with default",
550 bp: `
551 my_module_type {
552 name: "foo",
553 my_string: select(boolean_var_for_testing(), {
554 true: "t",
555 false: "f",
556 default: "default",
557 }),
558 }
559 `,
560 provider: selectsTestProvider{
561 my_string: proptools.StringPtr("default"),
562 },
563 },
564 {
565 name: "Mismatched condition types",
566 bp: `
567 my_module_type {
568 name: "foo",
569 my_string: select(boolean_var_for_testing(), {
570 "true": "t",
571 "false": "f",
572 default: "default",
573 }),
574 }
575 `,
576 vendorVars: map[string]map[string]string{
577 "boolean_var": {
578 "for_testing": "false",
579 },
580 },
581 expectedError: "Expected all branches of a select on condition boolean_var_for_testing\\(\\) to have type bool, found string",
582 },
Cole Faust5a231bd2024-02-07 09:43:59 -0800583 }
584
585 for _, tc := range testCases {
586 t.Run(tc.name, func(t *testing.T) {
587 fixtures := GroupFixturePreparers(
Cole Faust02dd6e52024-04-03 17:04:57 -0700588 PrepareForTestWithDefaults,
Cole Faust0aa21cc2024-03-20 12:28:03 -0700589 PrepareForTestWithArchMutator,
Cole Faust5a231bd2024-02-07 09:43:59 -0800590 FixtureRegisterWithContext(func(ctx RegistrationContext) {
591 ctx.RegisterModuleType("my_module_type", newSelectsMockModule)
Cole Faust02dd6e52024-04-03 17:04:57 -0700592 ctx.RegisterModuleType("my_defaults", newSelectsMockModuleDefaults)
Cole Faust5a231bd2024-02-07 09:43:59 -0800593 }),
594 FixtureModifyProductVariables(func(variables FixtureProductVariables) {
595 variables.VendorVars = tc.vendorVars
596 }),
597 )
598 if tc.expectedError != "" {
599 fixtures = fixtures.ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(tc.expectedError))
600 }
601 result := fixtures.RunTestWithBp(t, tc.bp)
602
603 if tc.expectedError == "" {
Cole Faust0aa21cc2024-03-20 12:28:03 -0700604 m := result.ModuleForTests("foo", "android_arm64_armv8-a")
605 p, _ := OtherModuleProvider(result.testContext.OtherModuleProviderAdaptor(), m.Module(), selectsTestProviderKey)
Cole Faust5a231bd2024-02-07 09:43:59 -0800606 if !reflect.DeepEqual(p, tc.provider) {
607 t.Errorf("Expected:\n %q\ngot:\n %q", tc.provider.String(), p.String())
608 }
609 }
610 })
611 }
612}
613
614type selectsTestProvider struct {
Cole Faust02dd6e52024-04-03 17:04:57 -0700615 my_bool *bool
616 my_string *string
617 my_string_list *[]string
618 my_paths *[]string
619 replacing_string_list *[]string
Cole Faust5a231bd2024-02-07 09:43:59 -0800620}
621
622func (p *selectsTestProvider) String() string {
623 myBoolStr := "nil"
624 if p.my_bool != nil {
625 myBoolStr = fmt.Sprintf("%t", *p.my_bool)
626 }
627 myStringStr := "nil"
628 if p.my_string != nil {
629 myStringStr = *p.my_string
630 }
631 return fmt.Sprintf(`selectsTestProvider {
632 my_bool: %v,
633 my_string: %s,
634 my_string_list: %s,
Cole Faustbdd8aee2024-03-14 14:33:02 -0700635 my_paths: %s,
Cole Faust02dd6e52024-04-03 17:04:57 -0700636 replacing_string_list %s,
637}`, myBoolStr, myStringStr, p.my_string_list, p.my_paths, p.replacing_string_list)
Cole Faust5a231bd2024-02-07 09:43:59 -0800638}
639
640var selectsTestProviderKey = blueprint.NewProvider[selectsTestProvider]()
641
642type selectsMockModuleProperties struct {
Cole Faust02dd6e52024-04-03 17:04:57 -0700643 My_bool proptools.Configurable[bool]
644 My_string proptools.Configurable[string]
645 My_string_list proptools.Configurable[[]string]
646 My_paths proptools.Configurable[[]string] `android:"path"`
647 Replacing_string_list proptools.Configurable[[]string] `android:"replace_instead_of_append,arch_variant"`
Cole Faust5a231bd2024-02-07 09:43:59 -0800648}
649
650type selectsMockModule struct {
651 ModuleBase
652 DefaultableModuleBase
653 properties selectsMockModuleProperties
654}
655
656func (p *selectsMockModule) GenerateAndroidBuildActions(ctx ModuleContext) {
Cole Faustbdd8aee2024-03-14 14:33:02 -0700657 SetProvider(ctx, selectsTestProviderKey, selectsTestProvider{
Cole Faustb78ce432024-04-04 10:55:19 -0700658 my_bool: p.properties.My_bool.Get(ctx),
659 my_string: p.properties.My_string.Get(ctx),
660 my_string_list: p.properties.My_string_list.Get(ctx),
661 my_paths: p.properties.My_paths.Get(ctx),
662 replacing_string_list: p.properties.Replacing_string_list.Get(ctx),
Cole Faust5a231bd2024-02-07 09:43:59 -0800663 })
664}
665
666func newSelectsMockModule() Module {
667 m := &selectsMockModule{}
668 m.AddProperties(&m.properties)
Cole Faust0aa21cc2024-03-20 12:28:03 -0700669 InitAndroidArchModule(m, HostAndDeviceSupported, MultilibFirst)
Cole Faust5a231bd2024-02-07 09:43:59 -0800670 InitDefaultableModule(m)
671 return m
672}
Cole Faust02dd6e52024-04-03 17:04:57 -0700673
674type selectsMockModuleDefaults struct {
675 ModuleBase
676 DefaultsModuleBase
677}
678
679func (d *selectsMockModuleDefaults) GenerateAndroidBuildActions(ctx ModuleContext) {
680}
681
682func newSelectsMockModuleDefaults() Module {
683 module := &selectsMockModuleDefaults{}
684
685 module.AddProperties(
686 &selectsMockModuleProperties{},
687 )
688
689 InitDefaultsModule(module)
690
691 return module
692}