blob: 8e469f8e364914fa0f5849059c2ba38988407eb3 [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 {
Cole Faust46f6e2f2024-06-20 12:57:43 -070028 name string
29 bp string
Cole Faust165a05b2024-06-20 18:17:04 -070030 fs MockFS
Cole Faust46f6e2f2024-06-20 12:57:43 -070031 provider selectsTestProvider
32 providers map[string]selectsTestProvider
33 vendorVars map[string]map[string]string
34 vendorVarTypes map[string]map[string]string
35 expectedError string
Cole Faust5a231bd2024-02-07 09:43:59 -080036 }{
37 {
38 name: "basic string list",
39 bp: `
40 my_module_type {
41 name: "foo",
42 my_string_list: select(soong_config_variable("my_namespace", "my_variable"), {
43 "a": ["a.cpp"],
44 "b": ["b.cpp"],
Cole Faust683316a2024-04-02 16:45:54 -070045 default: ["c.cpp"],
Cole Faust5a231bd2024-02-07 09:43:59 -080046 }),
47 }
48 `,
49 provider: selectsTestProvider{
50 my_string_list: &[]string{"c.cpp"},
51 },
52 },
53 {
54 name: "basic string",
55 bp: `
56 my_module_type {
57 name: "foo",
58 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
59 "a": "a.cpp",
60 "b": "b.cpp",
Cole Faust683316a2024-04-02 16:45:54 -070061 default: "c.cpp",
Cole Faust5a231bd2024-02-07 09:43:59 -080062 }),
63 }
64 `,
65 provider: selectsTestProvider{
66 my_string: proptools.StringPtr("c.cpp"),
67 },
68 },
69 {
70 name: "basic bool",
71 bp: `
72 my_module_type {
73 name: "foo",
74 my_bool: select(soong_config_variable("my_namespace", "my_variable"), {
75 "a": true,
76 "b": false,
Cole Faust683316a2024-04-02 16:45:54 -070077 default: true,
Cole Faust5a231bd2024-02-07 09:43:59 -080078 }),
79 }
80 `,
81 provider: selectsTestProvider{
82 my_bool: proptools.BoolPtr(true),
83 },
84 },
85 {
Cole Faustbdd8aee2024-03-14 14:33:02 -070086 name: "basic paths",
87 bp: `
88 my_module_type {
89 name: "foo",
90 my_paths: select(soong_config_variable("my_namespace", "my_variable"), {
91 "a": ["foo.txt"],
92 "b": ["bar.txt"],
Cole Faust683316a2024-04-02 16:45:54 -070093 default: ["baz.txt"],
Cole Faustbdd8aee2024-03-14 14:33:02 -070094 }),
95 }
96 `,
97 provider: selectsTestProvider{
98 my_paths: &[]string{"baz.txt"},
99 },
100 },
101 {
Cole Faustba483662024-06-17 15:03:33 -0700102 name: "Expression in select",
103 bp: `
104 my_module_type {
105 name: "foo",
106 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
107 "a": "foo" + "bar",
108 default: "baz",
109 }),
110 }
111 `,
112 provider: selectsTestProvider{
113 my_string: proptools.StringPtr("foobar"),
114 },
115 vendorVars: map[string]map[string]string{
116 "my_namespace": {
117 "my_variable": "a",
118 },
119 },
120 },
121 {
Cole Faustbdd8aee2024-03-14 14:33:02 -0700122 name: "paths with module references",
123 bp: `
124 my_module_type {
125 name: "foo",
126 my_paths: select(soong_config_variable("my_namespace", "my_variable"), {
127 "a": [":a"],
128 "b": [":b"],
Cole Faust683316a2024-04-02 16:45:54 -0700129 default: [":c"],
Cole Faustbdd8aee2024-03-14 14:33:02 -0700130 }),
131 }
132 `,
133 expectedError: `"foo" depends on undefined module "c"`,
134 },
135 {
Cole Faust12c8ed42024-03-28 16:26:59 -0700136 name: "Select type doesn't match property type",
137 bp: `
138 my_module_type {
139 name: "foo",
140 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
141 "a": false,
142 "b": true,
Cole Faust683316a2024-04-02 16:45:54 -0700143 default: true,
Cole Faust12c8ed42024-03-28 16:26:59 -0700144 }),
145 }
146 `,
Cole Faustba483662024-06-17 15:03:33 -0700147 expectedError: `can't assign bool value to string property`,
Cole Faust5a231bd2024-02-07 09:43:59 -0800148 },
149 {
150 name: "String list non-default",
151 bp: `
152 my_module_type {
153 name: "foo",
154 my_string_list: select(soong_config_variable("my_namespace", "my_variable"), {
155 "a": ["a.cpp"],
156 "b": ["b.cpp"],
Cole Faust683316a2024-04-02 16:45:54 -0700157 default: ["c.cpp"],
Cole Faust5a231bd2024-02-07 09:43:59 -0800158 }),
159 }
160 `,
161 provider: selectsTestProvider{
162 my_string_list: &[]string{"a.cpp"},
163 },
164 vendorVars: map[string]map[string]string{
165 "my_namespace": {
166 "my_variable": "a",
167 },
168 },
169 },
170 {
171 name: "String list append",
172 bp: `
173 my_module_type {
174 name: "foo",
175 my_string_list: select(soong_config_variable("my_namespace", "my_variable"), {
176 "a": ["a.cpp"],
177 "b": ["b.cpp"],
Cole Faust683316a2024-04-02 16:45:54 -0700178 default: ["c.cpp"],
Cole Faust5a231bd2024-02-07 09:43:59 -0800179 }) + select(soong_config_variable("my_namespace", "my_variable_2"), {
180 "a2": ["a2.cpp"],
181 "b2": ["b2.cpp"],
Cole Faust683316a2024-04-02 16:45:54 -0700182 default: ["c2.cpp"],
Cole Faust5a231bd2024-02-07 09:43:59 -0800183 }),
184 }
185 `,
186 provider: selectsTestProvider{
187 my_string_list: &[]string{"a.cpp", "c2.cpp"},
188 },
189 vendorVars: map[string]map[string]string{
190 "my_namespace": {
191 "my_variable": "a",
192 },
193 },
194 },
195 {
196 name: "String list prepend literal",
197 bp: `
198 my_module_type {
199 name: "foo",
200 my_string_list: ["literal.cpp"] + select(soong_config_variable("my_namespace", "my_variable"), {
201 "a2": ["a2.cpp"],
202 "b2": ["b2.cpp"],
Cole Faust683316a2024-04-02 16:45:54 -0700203 default: ["c2.cpp"],
Cole Faust5a231bd2024-02-07 09:43:59 -0800204 }),
205 }
206 `,
207 provider: selectsTestProvider{
208 my_string_list: &[]string{"literal.cpp", "c2.cpp"},
209 },
210 },
211 {
212 name: "String list append literal",
213 bp: `
214 my_module_type {
215 name: "foo",
216 my_string_list: select(soong_config_variable("my_namespace", "my_variable"), {
217 "a2": ["a2.cpp"],
218 "b2": ["b2.cpp"],
Cole Faust683316a2024-04-02 16:45:54 -0700219 default: ["c2.cpp"],
Cole Faust5a231bd2024-02-07 09:43:59 -0800220 }) + ["literal.cpp"],
221 }
222 `,
223 provider: selectsTestProvider{
224 my_string_list: &[]string{"c2.cpp", "literal.cpp"},
225 },
226 },
227 {
Cole Faust74ef4652024-03-27 16:45:41 -0700228 name: "true + false = true",
Cole Faust5a231bd2024-02-07 09:43:59 -0800229 bp: `
230 my_module_type {
231 name: "foo",
232 my_bool: select(soong_config_variable("my_namespace", "my_variable"), {
233 "a": true,
234 "b": false,
Cole Faust683316a2024-04-02 16:45:54 -0700235 default: true,
Cole Faust5a231bd2024-02-07 09:43:59 -0800236 }) + false,
237 }
238 `,
Cole Faust74ef4652024-03-27 16:45:41 -0700239 provider: selectsTestProvider{
240 my_bool: proptools.BoolPtr(true),
241 },
242 },
243 {
244 name: "false + false = false",
245 bp: `
246 my_module_type {
247 name: "foo",
248 my_bool: select(soong_config_variable("my_namespace", "my_variable"), {
249 "a": true,
250 "b": false,
Cole Faust683316a2024-04-02 16:45:54 -0700251 default: true,
Cole Faust74ef4652024-03-27 16:45:41 -0700252 }) + false,
253 }
254 `,
255 vendorVars: map[string]map[string]string{
256 "my_namespace": {
257 "my_variable": "b",
258 },
259 },
260 provider: selectsTestProvider{
261 my_bool: proptools.BoolPtr(false),
262 },
Cole Faust5a231bd2024-02-07 09:43:59 -0800263 },
264 {
265 name: "Append string",
266 bp: `
267 my_module_type {
268 name: "foo",
269 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
270 "a": "a",
271 "b": "b",
Cole Faust683316a2024-04-02 16:45:54 -0700272 default: "c",
Cole Faust5a231bd2024-02-07 09:43:59 -0800273 }) + ".cpp",
274 }
275 `,
276 provider: selectsTestProvider{
277 my_string: proptools.StringPtr("c.cpp"),
278 },
279 },
Cole Faust0aa21cc2024-03-20 12:28:03 -0700280 {
Cole Faustfc57d402024-04-11 12:09:44 -0700281 name: "Select on arch",
Cole Faust0aa21cc2024-03-20 12:28:03 -0700282 bp: `
283 my_module_type {
284 name: "foo",
Cole Faustfc57d402024-04-11 12:09:44 -0700285 my_string: select(arch(), {
Cole Faust0aa21cc2024-03-20 12:28:03 -0700286 "x86": "my_x86",
287 "x86_64": "my_x86_64",
288 "arm": "my_arm",
289 "arm64": "my_arm64",
Cole Faust683316a2024-04-02 16:45:54 -0700290 default: "my_default",
Cole Faust0aa21cc2024-03-20 12:28:03 -0700291 }),
292 }
293 `,
294 provider: selectsTestProvider{
295 my_string: proptools.StringPtr("my_arm64"),
296 },
297 },
Cole Faust12c8ed42024-03-28 16:26:59 -0700298 {
Cole Faustfc57d402024-04-11 12:09:44 -0700299 name: "Select on os",
300 bp: `
301 my_module_type {
302 name: "foo",
303 my_string: select(os(), {
304 "android": "my_android",
305 "linux": "my_linux",
306 default: "my_default",
307 }),
308 }
309 `,
310 provider: selectsTestProvider{
311 my_string: proptools.StringPtr("my_android"),
312 },
313 },
314 {
Cole Faust12c8ed42024-03-28 16:26:59 -0700315 name: "Unset value",
316 bp: `
317 my_module_type {
318 name: "foo",
319 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
320 "a": unset,
321 "b": "b",
Cole Faust683316a2024-04-02 16:45:54 -0700322 default: "c",
Cole Faust12c8ed42024-03-28 16:26:59 -0700323 })
324 }
325 `,
326 vendorVars: map[string]map[string]string{
327 "my_namespace": {
328 "my_variable": "a",
329 },
330 },
331 provider: selectsTestProvider{},
332 },
333 {
334 name: "Unset value on different branch",
335 bp: `
336 my_module_type {
337 name: "foo",
338 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
339 "a": unset,
340 "b": "b",
Cole Faust683316a2024-04-02 16:45:54 -0700341 default: "c",
Cole Faust12c8ed42024-03-28 16:26:59 -0700342 })
343 }
344 `,
345 provider: selectsTestProvider{
346 my_string: proptools.StringPtr("c"),
347 },
348 },
349 {
350 name: "unset + unset = unset",
351 bp: `
352 my_module_type {
353 name: "foo",
354 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
Cole Faustfdbf5d42024-04-10 15:01:23 -0700355 "foo": "bar",
Cole Faust683316a2024-04-02 16:45:54 -0700356 default: unset,
Cole Faust12c8ed42024-03-28 16:26:59 -0700357 }) + select(soong_config_variable("my_namespace", "my_variable2"), {
Cole Faustfdbf5d42024-04-10 15:01:23 -0700358 "baz": "qux",
Cole Faust683316a2024-04-02 16:45:54 -0700359 default: unset,
Cole Faust12c8ed42024-03-28 16:26:59 -0700360 })
361 }
362 `,
363 provider: selectsTestProvider{},
364 },
365 {
366 name: "unset + string = string",
367 bp: `
368 my_module_type {
369 name: "foo",
370 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
Cole Faustfdbf5d42024-04-10 15:01:23 -0700371 "foo": "bar",
Cole Faust683316a2024-04-02 16:45:54 -0700372 default: unset,
Cole Faust12c8ed42024-03-28 16:26:59 -0700373 }) + select(soong_config_variable("my_namespace", "my_variable2"), {
Cole Faust683316a2024-04-02 16:45:54 -0700374 default: "a",
Cole Faust12c8ed42024-03-28 16:26:59 -0700375 })
376 }
377 `,
378 provider: selectsTestProvider{
379 my_string: proptools.StringPtr("a"),
380 },
381 },
382 {
383 name: "unset + bool = bool",
384 bp: `
385 my_module_type {
386 name: "foo",
387 my_bool: select(soong_config_variable("my_namespace", "my_variable"), {
388 "a": true,
Cole Faust683316a2024-04-02 16:45:54 -0700389 default: unset,
Cole Faust12c8ed42024-03-28 16:26:59 -0700390 }) + select(soong_config_variable("my_namespace", "my_variable2"), {
Cole Faust683316a2024-04-02 16:45:54 -0700391 default: true,
Cole Faust12c8ed42024-03-28 16:26:59 -0700392 })
393 }
394 `,
395 provider: selectsTestProvider{
396 my_bool: proptools.BoolPtr(true),
397 },
398 },
Cole Faust02dd6e52024-04-03 17:04:57 -0700399 {
400 name: "defaults with lists are appended",
401 bp: `
402 my_module_type {
403 name: "foo",
404 defaults: ["bar"],
405 my_string_list: select(soong_config_variable("my_namespace", "my_variable"), {
406 "a": ["a1"],
407 default: ["b1"],
408 }),
409 }
410 my_defaults {
411 name: "bar",
412 my_string_list: select(soong_config_variable("my_namespace", "my_variable2"), {
413 "a": ["a2"],
414 default: ["b2"],
415 }),
416 }
417 `,
418 provider: selectsTestProvider{
419 my_string_list: &[]string{"b2", "b1"},
420 },
421 },
422 {
Cole Faust69349462024-04-25 16:02:15 -0700423 name: "defaults applied to multiple modules",
424 bp: `
425 my_module_type {
426 name: "foo2",
427 defaults: ["bar"],
428 my_string_list: select(soong_config_variable("my_namespace", "my_variable"), {
429 "a": ["a1"],
430 default: ["b1"],
431 }),
432 }
433 my_module_type {
434 name: "foo",
435 defaults: ["bar"],
436 my_string_list: select(soong_config_variable("my_namespace", "my_variable"), {
437 "a": ["a1"],
438 default: ["b1"],
439 }),
440 }
441 my_defaults {
442 name: "bar",
443 my_string_list: select(soong_config_variable("my_namespace", "my_variable2"), {
444 "a": ["a2"],
445 default: ["b2"],
446 }),
447 }
448 `,
449 providers: map[string]selectsTestProvider{
450 "foo": {
451 my_string_list: &[]string{"b2", "b1"},
452 },
453 "foo2": {
454 my_string_list: &[]string{"b2", "b1"},
455 },
456 },
457 },
458 {
Cole Faust02dd6e52024-04-03 17:04:57 -0700459 name: "Replacing string list",
460 bp: `
461 my_module_type {
462 name: "foo",
463 defaults: ["bar"],
464 replacing_string_list: select(soong_config_variable("my_namespace", "my_variable"), {
465 "a": ["a1"],
466 default: ["b1"],
467 }),
468 }
469 my_defaults {
470 name: "bar",
471 replacing_string_list: select(soong_config_variable("my_namespace", "my_variable2"), {
472 "a": ["a2"],
473 default: ["b2"],
474 }),
475 }
476 `,
477 provider: selectsTestProvider{
478 replacing_string_list: &[]string{"b1"},
479 },
480 },
Cole Faustfdbf5d42024-04-10 15:01:23 -0700481 {
482 name: "Multi-condition string 1",
483 bp: `
484 my_module_type {
485 name: "foo",
486 my_string: select((
487 soong_config_variable("my_namespace", "my_variable"),
488 soong_config_variable("my_namespace", "my_variable2"),
489 ), {
490 ("a", "b"): "a+b",
491 ("a", default): "a+default",
492 (default, default): "default",
493 }),
494 }
495 `,
496 vendorVars: map[string]map[string]string{
497 "my_namespace": {
498 "my_variable": "a",
499 "my_variable2": "b",
500 },
501 },
502 provider: selectsTestProvider{
503 my_string: proptools.StringPtr("a+b"),
504 },
505 },
506 {
507 name: "Multi-condition string 2",
508 bp: `
509 my_module_type {
510 name: "foo",
511 my_string: select((
512 soong_config_variable("my_namespace", "my_variable"),
513 soong_config_variable("my_namespace", "my_variable2"),
514 ), {
515 ("a", "b"): "a+b",
516 ("a", default): "a+default",
517 (default, default): "default",
518 }),
519 }
520 `,
521 vendorVars: map[string]map[string]string{
522 "my_namespace": {
523 "my_variable": "a",
524 "my_variable2": "c",
525 },
526 },
527 provider: selectsTestProvider{
528 my_string: proptools.StringPtr("a+default"),
529 },
530 },
531 {
532 name: "Multi-condition string 3",
533 bp: `
534 my_module_type {
535 name: "foo",
536 my_string: select((
537 soong_config_variable("my_namespace", "my_variable"),
538 soong_config_variable("my_namespace", "my_variable2"),
539 ), {
540 ("a", "b"): "a+b",
541 ("a", default): "a+default",
542 (default, default): "default",
543 }),
544 }
545 `,
546 vendorVars: map[string]map[string]string{
547 "my_namespace": {
548 "my_variable": "c",
549 "my_variable2": "b",
550 },
551 },
552 provider: selectsTestProvider{
553 my_string: proptools.StringPtr("default"),
554 },
555 },
556 {
Cole Faustb81dc0e2024-05-09 15:51:52 -0700557 name: "Unhandled string value",
558 bp: `
559 my_module_type {
560 name: "foo",
561 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
562 "foo": "a",
563 "bar": "b",
564 }),
565 }
566 `,
567 vendorVars: map[string]map[string]string{
568 "my_namespace": {
569 "my_variable": "baz",
570 },
571 },
572 expectedError: `my_string: soong_config_variable\("my_namespace", "my_variable"\) had value "baz", which was not handled by the select statement`,
573 },
574 {
Cole Faustfdbf5d42024-04-10 15:01:23 -0700575 name: "Select on boolean",
576 bp: `
577 my_module_type {
578 name: "foo",
579 my_string: select(boolean_var_for_testing(), {
580 true: "t",
581 false: "f",
582 }),
583 }
584 `,
585 vendorVars: map[string]map[string]string{
586 "boolean_var": {
587 "for_testing": "true",
588 },
589 },
590 provider: selectsTestProvider{
591 my_string: proptools.StringPtr("t"),
592 },
593 },
594 {
Cole Faust46f6e2f2024-06-20 12:57:43 -0700595 name: "Select on boolean soong config variable",
596 bp: `
597 my_module_type {
598 name: "foo",
599 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
600 true: "t",
601 false: "f",
602 }),
603 }
604 `,
605 vendorVars: map[string]map[string]string{
606 "my_namespace": {
607 "my_variable": "true",
608 },
609 },
610 vendorVarTypes: map[string]map[string]string{
611 "my_namespace": {
612 "my_variable": "bool",
613 },
614 },
615 provider: selectsTestProvider{
616 my_string: proptools.StringPtr("t"),
617 },
618 },
619 {
Cole Faustfdbf5d42024-04-10 15:01:23 -0700620 name: "Select on boolean false",
621 bp: `
622 my_module_type {
623 name: "foo",
624 my_string: select(boolean_var_for_testing(), {
625 true: "t",
626 false: "f",
627 }),
628 }
629 `,
630 vendorVars: map[string]map[string]string{
631 "boolean_var": {
632 "for_testing": "false",
633 },
634 },
635 provider: selectsTestProvider{
636 my_string: proptools.StringPtr("f"),
637 },
638 },
639 {
640 name: "Select on boolean undefined",
641 bp: `
642 my_module_type {
643 name: "foo",
644 my_string: select(boolean_var_for_testing(), {
645 true: "t",
646 false: "f",
647 }),
648 }
649 `,
Cole Faustb81dc0e2024-05-09 15:51:52 -0700650 expectedError: `my_string: boolean_var_for_testing\(\) had value undefined, which was not handled by the select statement`,
Cole Faustfdbf5d42024-04-10 15:01:23 -0700651 },
652 {
653 name: "Select on boolean undefined with default",
654 bp: `
655 my_module_type {
656 name: "foo",
657 my_string: select(boolean_var_for_testing(), {
658 true: "t",
659 false: "f",
660 default: "default",
661 }),
662 }
663 `,
664 provider: selectsTestProvider{
665 my_string: proptools.StringPtr("default"),
666 },
667 },
668 {
Cole Faust764aaca2025-03-18 11:24:39 -0700669 name: "Select on integer soong config variable",
670 bp: `
671 my_module_type {
672 name: "foo",
673 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
674 34: "34",
675 default: "other",
676 }),
677 }
678 `,
679 vendorVars: map[string]map[string]string{
680 "my_namespace": {
681 "my_variable": "34",
682 },
683 },
684 vendorVarTypes: map[string]map[string]string{
685 "my_namespace": {
686 "my_variable": "int",
687 },
688 },
689 provider: selectsTestProvider{
690 my_string: proptools.StringPtr("34"),
691 },
692 },
693 {
694 name: "Select on integer soong config variable default",
695 bp: `
696 my_module_type {
697 name: "foo",
698 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
699 34: "34",
700 default: "other",
701 }),
702 }
703 `,
704 vendorVars: map[string]map[string]string{
705 "my_namespace": {
706 "my_variable": "5",
707 },
708 },
709 vendorVarTypes: map[string]map[string]string{
710 "my_namespace": {
711 "my_variable": "int",
712 },
713 },
714 provider: selectsTestProvider{
715 my_string: proptools.StringPtr("other"),
716 },
717 },
718 {
719 name: "Assign to integer property",
720 bp: `
721 my_module_type {
722 name: "foo",
723 my_int64: select(soong_config_variable("my_namespace", "my_variable"), {
724 any @ val: val,
725 default: "other",
726 }),
727 }
728 `,
729 vendorVars: map[string]map[string]string{
730 "my_namespace": {
731 "my_variable": "5",
732 },
733 },
734 vendorVarTypes: map[string]map[string]string{
735 "my_namespace": {
736 "my_variable": "int",
737 },
738 },
739 provider: selectsTestProvider{
740 my_int64: proptools.Int64Ptr(5),
741 },
742 },
743 {
Cole Faustfdbf5d42024-04-10 15:01:23 -0700744 name: "Mismatched condition types",
745 bp: `
746 my_module_type {
747 name: "foo",
748 my_string: select(boolean_var_for_testing(), {
749 "true": "t",
750 "false": "f",
751 default: "default",
752 }),
753 }
754 `,
755 vendorVars: map[string]map[string]string{
756 "boolean_var": {
757 "for_testing": "false",
758 },
759 },
760 expectedError: "Expected all branches of a select on condition boolean_var_for_testing\\(\\) to have type bool, found string",
761 },
Cole Faust60f6bb22024-04-30 13:58:55 -0700762 {
763 name: "Assigning select to nonconfigurable bool",
764 bp: `
765 my_module_type {
766 name: "foo",
767 my_nonconfigurable_bool: select(arch(), {
768 "x86_64": true,
769 default: false,
770 }),
771 }
772 `,
773 expectedError: `can't assign select statement to non-configurable property "my_nonconfigurable_bool"`,
774 },
775 {
776 name: "Assigning select to nonconfigurable string",
777 bp: `
778 my_module_type {
779 name: "foo",
780 my_nonconfigurable_string: select(arch(), {
781 "x86_64": "x86!",
782 default: "unknown!",
783 }),
784 }
785 `,
786 expectedError: `can't assign select statement to non-configurable property "my_nonconfigurable_string"`,
787 },
788 {
789 name: "Assigning appended selects to nonconfigurable string",
790 bp: `
791 my_module_type {
792 name: "foo",
793 my_nonconfigurable_string: select(arch(), {
794 "x86_64": "x86!",
795 default: "unknown!",
796 }) + select(os(), {
797 "darwin": "_darwin!",
798 default: "unknown!",
799 }),
800 }
801 `,
802 expectedError: `can't assign select statement to non-configurable property "my_nonconfigurable_string"`,
803 },
804 {
805 name: "Assigning select to nonconfigurable string list",
806 bp: `
807 my_module_type {
808 name: "foo",
809 my_nonconfigurable_string_list: select(arch(), {
810 "x86_64": ["foo", "bar"],
811 default: ["baz", "qux"],
812 }),
813 }
814 `,
815 expectedError: `can't assign select statement to non-configurable property "my_nonconfigurable_string_list"`,
816 },
Cole Faustb9519092024-05-21 11:20:15 -0700817 {
818 name: "Select in variable",
819 bp: `
820 my_second_variable = ["after.cpp"]
821 my_variable = select(soong_config_variable("my_namespace", "my_variable"), {
822 "a": ["a.cpp"],
823 "b": ["b.cpp"],
824 default: ["c.cpp"],
825 }) + my_second_variable
826 my_module_type {
827 name: "foo",
828 my_string_list: ["before.cpp"] + my_variable,
829 }
830 `,
831 provider: selectsTestProvider{
832 my_string_list: &[]string{"before.cpp", "a.cpp", "after.cpp"},
833 },
834 vendorVars: map[string]map[string]string{
835 "my_namespace": {
836 "my_variable": "a",
837 },
838 },
839 },
Cole Faust5f297062024-05-22 14:30:16 -0700840 {
841 name: "Soong config value variable on configurable property",
842 bp: `
843 soong_config_module_type {
844 name: "soong_config_my_module_type",
845 module_type: "my_module_type",
846 config_namespace: "my_namespace",
847 value_variables: ["my_variable"],
848 properties: ["my_string", "my_string_list"],
849 }
850
851 soong_config_my_module_type {
852 name: "foo",
853 my_string_list: ["before.cpp"],
854 soong_config_variables: {
855 my_variable: {
856 my_string_list: ["after_%s.cpp"],
857 my_string: "%s.cpp",
858 },
859 },
860 }
861 `,
862 provider: selectsTestProvider{
863 my_string: proptools.StringPtr("foo.cpp"),
864 my_string_list: &[]string{"before.cpp", "after_foo.cpp"},
865 },
866 vendorVars: map[string]map[string]string{
867 "my_namespace": {
868 "my_variable": "foo",
869 },
870 },
871 },
Cole Faustaeecb752024-05-22 13:41:35 -0700872 {
873 name: "Property appending with variable",
874 bp: `
875 my_variable = ["b.cpp"]
876 my_module_type {
877 name: "foo",
878 my_string_list: ["a.cpp"] + my_variable + select(soong_config_variable("my_namespace", "my_variable"), {
879 "a": ["a.cpp"],
880 "b": ["b.cpp"],
881 default: ["c.cpp"],
882 }),
883 }
884 `,
885 provider: selectsTestProvider{
886 my_string_list: &[]string{"a.cpp", "b.cpp", "c.cpp"},
887 },
888 },
Cole Faustfee6fde2024-06-13 15:35:17 -0700889 {
890 name: "Test AppendSimpleValue",
891 bp: `
892 my_module_type {
893 name: "foo",
894 my_string_list: ["a.cpp"] + select(soong_config_variable("my_namespace", "my_variable"), {
895 "a": ["a.cpp"],
896 "b": ["b.cpp"],
897 default: ["c.cpp"],
898 }),
899 }
900 `,
901 vendorVars: map[string]map[string]string{
902 "selects_test": {
903 "append_to_string_list": "foo.cpp",
904 },
905 },
906 provider: selectsTestProvider{
907 my_string_list: &[]string{"a.cpp", "c.cpp", "foo.cpp"},
908 },
909 },
Cole Faustba483662024-06-17 15:03:33 -0700910 {
911 name: "Arch variant bool",
912 bp: `
913 my_variable = ["b.cpp"]
914 my_module_type {
915 name: "foo",
916 arch_variant_configurable_bool: false,
917 target: {
918 bionic_arm64: {
919 enabled: true,
920 },
921 },
922 }
923 `,
924 provider: selectsTestProvider{
925 arch_variant_configurable_bool: proptools.BoolPtr(false),
926 },
927 },
Cole Faust165a05b2024-06-20 18:17:04 -0700928 {
929 name: "Simple string binding",
930 bp: `
931 my_module_type {
932 name: "foo",
933 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
934 any @ my_binding: "hello " + my_binding,
935 default: "goodbye",
936 })
937 }
938 `,
939 vendorVars: map[string]map[string]string{
940 "my_namespace": {
941 "my_variable": "world!",
942 },
943 },
944 provider: selectsTestProvider{
945 my_string: proptools.StringPtr("hello world!"),
946 },
947 },
948 {
949 name: "Any branch with binding not taken",
950 bp: `
951 my_module_type {
952 name: "foo",
953 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
954 any @ my_binding: "hello " + my_binding,
955 default: "goodbye",
956 })
957 }
958 `,
959 provider: selectsTestProvider{
960 my_string: proptools.StringPtr("goodbye"),
961 },
962 },
963 {
964 name: "Any branch without binding",
965 bp: `
966 my_module_type {
967 name: "foo",
968 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
969 any: "hello",
970 default: "goodbye",
971 })
972 }
973 `,
974 vendorVars: map[string]map[string]string{
975 "my_namespace": {
976 "my_variable": "world!",
977 },
978 },
979 provider: selectsTestProvider{
980 my_string: proptools.StringPtr("hello"),
981 },
982 },
983 {
984 name: "Binding conflicts with file-level variable",
985 bp: `
986 my_binding = "asdf"
987 my_module_type {
988 name: "foo",
989 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
990 any @ my_binding: "hello",
991 default: "goodbye",
992 })
993 }
994 `,
995 vendorVars: map[string]map[string]string{
996 "my_namespace": {
997 "my_variable": "world!",
998 },
999 },
1000 expectedError: "variable already set in inherited scope, previous assignment",
1001 },
1002 {
1003 name: "Binding in combination with file-level variable",
1004 bp: `
1005 my_var = " there "
1006 my_module_type {
1007 name: "foo",
1008 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
1009 any @ my_binding: "hello" + my_var + my_binding,
1010 default: "goodbye",
1011 })
1012 }
1013 `,
1014 vendorVars: map[string]map[string]string{
1015 "my_namespace": {
1016 "my_variable": "world!",
1017 },
1018 },
1019 provider: selectsTestProvider{
1020 my_string: proptools.StringPtr("hello there world!"),
1021 },
1022 },
1023 {
1024 name: "Bindings in subdirectory inherits variable",
1025 fs: map[string][]byte{
1026 "Android.bp": []byte(`
1027my_var = "abcd"
1028`),
1029 "directoryB/Android.bp": []byte(`
1030my_module_type {
1031 name: "foo",
1032 my_string: select(soong_config_variable("my_namespace", "variable_a"), {
1033 any @ my_binding: my_var + my_binding,
1034 default: "",
1035 }),
1036}
1037`),
1038 },
1039 vendorVars: map[string]map[string]string{
1040 "my_namespace": {
1041 "variable_a": "e",
1042 },
1043 },
1044 provider: selectsTestProvider{
1045 my_string: proptools.StringPtr("abcde"),
1046 },
1047 },
1048 {
1049 name: "Cannot modify variable after referenced by select",
1050 bp: `
1051my_var = "foo"
1052my_module_type {
1053 name: "foo",
1054 my_string: select(soong_config_variable("my_namespace", "variable_a"), {
1055 "a": my_var,
1056 default: "",
1057 }),
1058}
1059my_var += "bar"
1060`,
1061 vendorVars: map[string]map[string]string{
1062 "my_namespace": {
1063 "variable_a": "b", // notably not the value that causes my_var to be referenced
1064 },
1065 },
1066 expectedError: `modified variable "my_var" with \+= after referencing`,
1067 },
1068 {
1069 name: "Cannot shadow variable with binding",
1070 bp: `
1071my_var = "foo"
1072my_module_type {
1073 name: "foo",
1074 my_string: select(soong_config_variable("my_namespace", "variable_a"), {
1075 any @ my_var: my_var,
1076 default: "",
1077 }),
1078}
1079`,
1080 vendorVars: map[string]map[string]string{
1081 "my_namespace": {
1082 "variable_a": "a",
1083 },
1084 },
1085 expectedError: `variable already set in inherited scope, previous assignment:`,
1086 },
Cole Faustb93f7fe2024-08-29 15:36:15 -07001087 {
1088 name: "Basic string list postprocessor",
1089 bp: `
1090my_defaults {
1091 name: "defaults_a",
1092 my_string_list: ["a", "b", "c"],
1093 string_list_postprocessor_add_to_elements: "1",
1094}
1095my_defaults {
1096 name: "defaults_b",
1097 my_string_list: ["d", "e", "f"],
1098 string_list_postprocessor_add_to_elements: "2",
1099}
1100my_module_type {
1101 name: "foo",
1102 defaults: ["defaults_a", "defaults_b"],
1103}
1104`,
1105 provider: selectsTestProvider{
1106 my_string_list: &[]string{"d2", "e2", "f2", "a1", "b1", "c1"},
1107 },
1108 },
Cole Faust4143ee82024-11-21 11:20:06 -08001109 {
1110 name: "string list variables",
1111 bp: `
1112my_module_type {
1113 name: "foo",
1114 my_string_list: ["a"] + select(soong_config_variable("my_namespace", "my_var"), {
1115 any @ my_var: my_var,
1116 default: [],
1117 }),
1118}
1119`,
1120 vendorVars: map[string]map[string]string{
1121 "my_namespace": {
1122 "my_var": "b c",
1123 },
1124 },
1125 vendorVarTypes: map[string]map[string]string{
1126 "my_namespace": {
1127 "my_var": "string_list",
1128 },
1129 },
1130 provider: selectsTestProvider{
1131 my_string_list: &[]string{"a", "b", "c"},
1132 },
1133 },
1134 {
1135 name: "string list variables don't match string matchers",
1136 bp: `
1137my_module_type {
1138 name: "foo",
1139 my_string_list: ["a"] + select(soong_config_variable("my_namespace", "my_var"), {
1140 "foo": ["b"],
1141 default: [],
1142 }),
1143}
1144`,
1145 vendorVars: map[string]map[string]string{
1146 "my_namespace": {
1147 "my_var": "b c",
1148 },
1149 },
1150 vendorVarTypes: map[string]map[string]string{
1151 "my_namespace": {
1152 "my_var": "string_list",
1153 },
1154 },
1155 expectedError: `Expected all branches of a select on condition soong_config_variable\("my_namespace", "my_var"\) to have type string_list, found string`,
1156 },
Cole Faust5a231bd2024-02-07 09:43:59 -08001157 }
1158
1159 for _, tc := range testCases {
1160 t.Run(tc.name, func(t *testing.T) {
Cole Faust165a05b2024-06-20 18:17:04 -07001161 fs := tc.fs
1162 if fs == nil {
1163 fs = make(MockFS)
1164 }
1165 if tc.bp != "" {
1166 fs["Android.bp"] = []byte(tc.bp)
1167 }
Cole Faust5a231bd2024-02-07 09:43:59 -08001168 fixtures := GroupFixturePreparers(
Cole Faust02dd6e52024-04-03 17:04:57 -07001169 PrepareForTestWithDefaults,
Cole Faust0aa21cc2024-03-20 12:28:03 -07001170 PrepareForTestWithArchMutator,
Cole Faust5f297062024-05-22 14:30:16 -07001171 PrepareForTestWithSoongConfigModuleBuildComponents,
Cole Faust5a231bd2024-02-07 09:43:59 -08001172 FixtureRegisterWithContext(func(ctx RegistrationContext) {
1173 ctx.RegisterModuleType("my_module_type", newSelectsMockModule)
Cole Faust02dd6e52024-04-03 17:04:57 -07001174 ctx.RegisterModuleType("my_defaults", newSelectsMockModuleDefaults)
Cole Faust5a231bd2024-02-07 09:43:59 -08001175 }),
1176 FixtureModifyProductVariables(func(variables FixtureProductVariables) {
1177 variables.VendorVars = tc.vendorVars
Cole Faust46f6e2f2024-06-20 12:57:43 -07001178 variables.VendorVarTypes = tc.vendorVarTypes
Cole Faust5a231bd2024-02-07 09:43:59 -08001179 }),
Cole Faust165a05b2024-06-20 18:17:04 -07001180 FixtureMergeMockFs(fs),
Cole Faust5a231bd2024-02-07 09:43:59 -08001181 )
1182 if tc.expectedError != "" {
1183 fixtures = fixtures.ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(tc.expectedError))
1184 }
Cole Faust165a05b2024-06-20 18:17:04 -07001185 result := fixtures.RunTest(t)
Cole Faust5a231bd2024-02-07 09:43:59 -08001186
1187 if tc.expectedError == "" {
Cole Faust69349462024-04-25 16:02:15 -07001188 if len(tc.providers) == 0 {
1189 tc.providers = map[string]selectsTestProvider{
1190 "foo": tc.provider,
1191 }
1192 }
1193
1194 for moduleName := range tc.providers {
1195 expected := tc.providers[moduleName]
Colin Cross90607e92025-02-11 14:58:07 -08001196 m := result.ModuleForTests(t, moduleName, "android_arm64_armv8-a")
Cole Faust69349462024-04-25 16:02:15 -07001197 p, _ := OtherModuleProvider(result.testContext.OtherModuleProviderAdaptor(), m.Module(), selectsTestProviderKey)
1198 if !reflect.DeepEqual(p, expected) {
1199 t.Errorf("Expected:\n %q\ngot:\n %q", expected.String(), p.String())
1200 }
Cole Faust5a231bd2024-02-07 09:43:59 -08001201 }
1202 }
1203 })
1204 }
1205}
1206
1207type selectsTestProvider struct {
Cole Faust60f6bb22024-04-30 13:58:55 -07001208 my_bool *bool
1209 my_string *string
Cole Faust764aaca2025-03-18 11:24:39 -07001210 my_int64 *int64
Cole Faust60f6bb22024-04-30 13:58:55 -07001211 my_string_list *[]string
1212 my_paths *[]string
1213 replacing_string_list *[]string
Cole Faustba483662024-06-17 15:03:33 -07001214 arch_variant_configurable_bool *bool
Cole Faust60f6bb22024-04-30 13:58:55 -07001215 my_nonconfigurable_bool *bool
1216 my_nonconfigurable_string *string
1217 my_nonconfigurable_string_list []string
Cole Faust5a231bd2024-02-07 09:43:59 -08001218}
1219
1220func (p *selectsTestProvider) String() string {
1221 myBoolStr := "nil"
1222 if p.my_bool != nil {
1223 myBoolStr = fmt.Sprintf("%t", *p.my_bool)
1224 }
1225 myStringStr := "nil"
1226 if p.my_string != nil {
1227 myStringStr = *p.my_string
1228 }
Cole Faust60f6bb22024-04-30 13:58:55 -07001229 myNonconfigurableStringStr := "nil"
Cole Faust5f297062024-05-22 14:30:16 -07001230 if p.my_nonconfigurable_string != nil {
Cole Faust60f6bb22024-04-30 13:58:55 -07001231 myNonconfigurableStringStr = *p.my_nonconfigurable_string
1232 }
Cole Faust5a231bd2024-02-07 09:43:59 -08001233 return fmt.Sprintf(`selectsTestProvider {
1234 my_bool: %v,
1235 my_string: %s,
1236 my_string_list: %s,
Cole Faustbdd8aee2024-03-14 14:33:02 -07001237 my_paths: %s,
Cole Faust02dd6e52024-04-03 17:04:57 -07001238 replacing_string_list %s,
Cole Faustba483662024-06-17 15:03:33 -07001239 arch_variant_configurable_bool %v
Cole Faust60f6bb22024-04-30 13:58:55 -07001240 my_nonconfigurable_bool: %v,
1241 my_nonconfigurable_string: %s,
1242 my_nonconfigurable_string_list: %s,
1243}`,
1244 myBoolStr,
1245 myStringStr,
1246 p.my_string_list,
1247 p.my_paths,
1248 p.replacing_string_list,
Cole Faustba483662024-06-17 15:03:33 -07001249 p.arch_variant_configurable_bool,
Cole Faust60f6bb22024-04-30 13:58:55 -07001250 p.my_nonconfigurable_bool,
1251 myNonconfigurableStringStr,
1252 p.my_nonconfigurable_string_list,
1253 )
Cole Faust5a231bd2024-02-07 09:43:59 -08001254}
1255
1256var selectsTestProviderKey = blueprint.NewProvider[selectsTestProvider]()
1257
1258type selectsMockModuleProperties struct {
Cole Faust60f6bb22024-04-30 13:58:55 -07001259 My_bool proptools.Configurable[bool]
Cole Faust764aaca2025-03-18 11:24:39 -07001260 My_int64 proptools.Configurable[int64]
Cole Faust60f6bb22024-04-30 13:58:55 -07001261 My_string proptools.Configurable[string]
1262 My_string_list proptools.Configurable[[]string]
1263 My_paths proptools.Configurable[[]string] `android:"path"`
1264 Replacing_string_list proptools.Configurable[[]string] `android:"replace_instead_of_append,arch_variant"`
Cole Faustba483662024-06-17 15:03:33 -07001265 Arch_variant_configurable_bool proptools.Configurable[bool] `android:"replace_instead_of_append,arch_variant"`
Cole Faust60f6bb22024-04-30 13:58:55 -07001266 My_nonconfigurable_bool *bool
1267 My_nonconfigurable_string *string
1268 My_nonconfigurable_string_list []string
Cole Faust5a231bd2024-02-07 09:43:59 -08001269}
1270
1271type selectsMockModule struct {
1272 ModuleBase
1273 DefaultableModuleBase
1274 properties selectsMockModuleProperties
1275}
1276
Cole Faust749eeaa2024-05-21 14:19:05 -07001277func optionalToPtr[T any](o proptools.ConfigurableOptional[T]) *T {
1278 if o.IsEmpty() {
1279 return nil
1280 }
1281 x := o.Get()
1282 return &x
1283}
1284
Cole Faust5a231bd2024-02-07 09:43:59 -08001285func (p *selectsMockModule) GenerateAndroidBuildActions(ctx ModuleContext) {
Cole Faustfee6fde2024-06-13 15:35:17 -07001286 toAppend := ctx.Config().VendorConfig("selects_test").String("append_to_string_list")
1287 if toAppend != "" {
1288 p.properties.My_string_list.AppendSimpleValue([]string{toAppend})
1289 }
Cole Faustbdd8aee2024-03-14 14:33:02 -07001290 SetProvider(ctx, selectsTestProviderKey, selectsTestProvider{
Cole Faust749eeaa2024-05-21 14:19:05 -07001291 my_bool: optionalToPtr(p.properties.My_bool.Get(ctx)),
1292 my_string: optionalToPtr(p.properties.My_string.Get(ctx)),
Cole Faust764aaca2025-03-18 11:24:39 -07001293 my_int64: optionalToPtr(p.properties.My_int64.Get(ctx)),
Cole Faust749eeaa2024-05-21 14:19:05 -07001294 my_string_list: optionalToPtr(p.properties.My_string_list.Get(ctx)),
1295 my_paths: optionalToPtr(p.properties.My_paths.Get(ctx)),
1296 replacing_string_list: optionalToPtr(p.properties.Replacing_string_list.Get(ctx)),
Cole Faustba483662024-06-17 15:03:33 -07001297 arch_variant_configurable_bool: optionalToPtr(p.properties.Arch_variant_configurable_bool.Get(ctx)),
Cole Faust60f6bb22024-04-30 13:58:55 -07001298 my_nonconfigurable_bool: p.properties.My_nonconfigurable_bool,
1299 my_nonconfigurable_string: p.properties.My_nonconfigurable_string,
1300 my_nonconfigurable_string_list: p.properties.My_nonconfigurable_string_list,
Cole Faust5a231bd2024-02-07 09:43:59 -08001301 })
1302}
1303
1304func newSelectsMockModule() Module {
1305 m := &selectsMockModule{}
1306 m.AddProperties(&m.properties)
Cole Faust0aa21cc2024-03-20 12:28:03 -07001307 InitAndroidArchModule(m, HostAndDeviceSupported, MultilibFirst)
Cole Faust5a231bd2024-02-07 09:43:59 -08001308 InitDefaultableModule(m)
1309 return m
1310}
Cole Faust02dd6e52024-04-03 17:04:57 -07001311
Cole Faustb93f7fe2024-08-29 15:36:15 -07001312type selectsMockDefaultsProperties struct {
1313 String_list_postprocessor_add_to_elements string
1314}
1315
Cole Faust02dd6e52024-04-03 17:04:57 -07001316type selectsMockModuleDefaults struct {
1317 ModuleBase
1318 DefaultsModuleBase
Cole Faustb93f7fe2024-08-29 15:36:15 -07001319 myProperties selectsMockModuleProperties
1320 defaultsProperties selectsMockDefaultsProperties
Cole Faust02dd6e52024-04-03 17:04:57 -07001321}
1322
1323func (d *selectsMockModuleDefaults) GenerateAndroidBuildActions(ctx ModuleContext) {
1324}
1325
1326func newSelectsMockModuleDefaults() Module {
1327 module := &selectsMockModuleDefaults{}
1328
1329 module.AddProperties(
Cole Faustb93f7fe2024-08-29 15:36:15 -07001330 &module.myProperties,
1331 &module.defaultsProperties,
Cole Faust02dd6e52024-04-03 17:04:57 -07001332 )
1333
1334 InitDefaultsModule(module)
1335
Cole Faustb93f7fe2024-08-29 15:36:15 -07001336 AddLoadHook(module, func(lhc LoadHookContext) {
1337 if module.defaultsProperties.String_list_postprocessor_add_to_elements != "" {
1338 module.myProperties.My_string_list.AddPostProcessor(func(x []string) []string {
1339 for i := range x {
1340 x[i] = x[i] + module.defaultsProperties.String_list_postprocessor_add_to_elements
1341 }
1342 return x
1343 })
1344 }
1345 })
1346
Cole Faust02dd6e52024-04-03 17:04:57 -07001347 return module
1348}