blob: 90d7091e0eca8442b7b3bee0a3645251e771363a [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 {
669 name: "Mismatched condition types",
670 bp: `
671 my_module_type {
672 name: "foo",
673 my_string: select(boolean_var_for_testing(), {
674 "true": "t",
675 "false": "f",
676 default: "default",
677 }),
678 }
679 `,
680 vendorVars: map[string]map[string]string{
681 "boolean_var": {
682 "for_testing": "false",
683 },
684 },
685 expectedError: "Expected all branches of a select on condition boolean_var_for_testing\\(\\) to have type bool, found string",
686 },
Cole Faust60f6bb22024-04-30 13:58:55 -0700687 {
688 name: "Assigning select to nonconfigurable bool",
689 bp: `
690 my_module_type {
691 name: "foo",
692 my_nonconfigurable_bool: select(arch(), {
693 "x86_64": true,
694 default: false,
695 }),
696 }
697 `,
698 expectedError: `can't assign select statement to non-configurable property "my_nonconfigurable_bool"`,
699 },
700 {
701 name: "Assigning select to nonconfigurable string",
702 bp: `
703 my_module_type {
704 name: "foo",
705 my_nonconfigurable_string: select(arch(), {
706 "x86_64": "x86!",
707 default: "unknown!",
708 }),
709 }
710 `,
711 expectedError: `can't assign select statement to non-configurable property "my_nonconfigurable_string"`,
712 },
713 {
714 name: "Assigning appended selects to nonconfigurable string",
715 bp: `
716 my_module_type {
717 name: "foo",
718 my_nonconfigurable_string: select(arch(), {
719 "x86_64": "x86!",
720 default: "unknown!",
721 }) + select(os(), {
722 "darwin": "_darwin!",
723 default: "unknown!",
724 }),
725 }
726 `,
727 expectedError: `can't assign select statement to non-configurable property "my_nonconfigurable_string"`,
728 },
729 {
730 name: "Assigning select to nonconfigurable string list",
731 bp: `
732 my_module_type {
733 name: "foo",
734 my_nonconfigurable_string_list: select(arch(), {
735 "x86_64": ["foo", "bar"],
736 default: ["baz", "qux"],
737 }),
738 }
739 `,
740 expectedError: `can't assign select statement to non-configurable property "my_nonconfigurable_string_list"`,
741 },
Cole Faustb9519092024-05-21 11:20:15 -0700742 {
743 name: "Select in variable",
744 bp: `
745 my_second_variable = ["after.cpp"]
746 my_variable = select(soong_config_variable("my_namespace", "my_variable"), {
747 "a": ["a.cpp"],
748 "b": ["b.cpp"],
749 default: ["c.cpp"],
750 }) + my_second_variable
751 my_module_type {
752 name: "foo",
753 my_string_list: ["before.cpp"] + my_variable,
754 }
755 `,
756 provider: selectsTestProvider{
757 my_string_list: &[]string{"before.cpp", "a.cpp", "after.cpp"},
758 },
759 vendorVars: map[string]map[string]string{
760 "my_namespace": {
761 "my_variable": "a",
762 },
763 },
764 },
Cole Faust5f297062024-05-22 14:30:16 -0700765 {
766 name: "Soong config value variable on configurable property",
767 bp: `
768 soong_config_module_type {
769 name: "soong_config_my_module_type",
770 module_type: "my_module_type",
771 config_namespace: "my_namespace",
772 value_variables: ["my_variable"],
773 properties: ["my_string", "my_string_list"],
774 }
775
776 soong_config_my_module_type {
777 name: "foo",
778 my_string_list: ["before.cpp"],
779 soong_config_variables: {
780 my_variable: {
781 my_string_list: ["after_%s.cpp"],
782 my_string: "%s.cpp",
783 },
784 },
785 }
786 `,
787 provider: selectsTestProvider{
788 my_string: proptools.StringPtr("foo.cpp"),
789 my_string_list: &[]string{"before.cpp", "after_foo.cpp"},
790 },
791 vendorVars: map[string]map[string]string{
792 "my_namespace": {
793 "my_variable": "foo",
794 },
795 },
796 },
Cole Faustaeecb752024-05-22 13:41:35 -0700797 {
798 name: "Property appending with variable",
799 bp: `
800 my_variable = ["b.cpp"]
801 my_module_type {
802 name: "foo",
803 my_string_list: ["a.cpp"] + my_variable + select(soong_config_variable("my_namespace", "my_variable"), {
804 "a": ["a.cpp"],
805 "b": ["b.cpp"],
806 default: ["c.cpp"],
807 }),
808 }
809 `,
810 provider: selectsTestProvider{
811 my_string_list: &[]string{"a.cpp", "b.cpp", "c.cpp"},
812 },
813 },
Cole Faustfee6fde2024-06-13 15:35:17 -0700814 {
815 name: "Test AppendSimpleValue",
816 bp: `
817 my_module_type {
818 name: "foo",
819 my_string_list: ["a.cpp"] + select(soong_config_variable("my_namespace", "my_variable"), {
820 "a": ["a.cpp"],
821 "b": ["b.cpp"],
822 default: ["c.cpp"],
823 }),
824 }
825 `,
826 vendorVars: map[string]map[string]string{
827 "selects_test": {
828 "append_to_string_list": "foo.cpp",
829 },
830 },
831 provider: selectsTestProvider{
832 my_string_list: &[]string{"a.cpp", "c.cpp", "foo.cpp"},
833 },
834 },
Cole Faustba483662024-06-17 15:03:33 -0700835 {
836 name: "Arch variant bool",
837 bp: `
838 my_variable = ["b.cpp"]
839 my_module_type {
840 name: "foo",
841 arch_variant_configurable_bool: false,
842 target: {
843 bionic_arm64: {
844 enabled: true,
845 },
846 },
847 }
848 `,
849 provider: selectsTestProvider{
850 arch_variant_configurable_bool: proptools.BoolPtr(false),
851 },
852 },
Cole Faust165a05b2024-06-20 18:17:04 -0700853 {
854 name: "Simple string binding",
855 bp: `
856 my_module_type {
857 name: "foo",
858 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
859 any @ my_binding: "hello " + my_binding,
860 default: "goodbye",
861 })
862 }
863 `,
864 vendorVars: map[string]map[string]string{
865 "my_namespace": {
866 "my_variable": "world!",
867 },
868 },
869 provider: selectsTestProvider{
870 my_string: proptools.StringPtr("hello world!"),
871 },
872 },
873 {
874 name: "Any branch with binding not taken",
875 bp: `
876 my_module_type {
877 name: "foo",
878 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
879 any @ my_binding: "hello " + my_binding,
880 default: "goodbye",
881 })
882 }
883 `,
884 provider: selectsTestProvider{
885 my_string: proptools.StringPtr("goodbye"),
886 },
887 },
888 {
889 name: "Any branch without binding",
890 bp: `
891 my_module_type {
892 name: "foo",
893 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
894 any: "hello",
895 default: "goodbye",
896 })
897 }
898 `,
899 vendorVars: map[string]map[string]string{
900 "my_namespace": {
901 "my_variable": "world!",
902 },
903 },
904 provider: selectsTestProvider{
905 my_string: proptools.StringPtr("hello"),
906 },
907 },
908 {
909 name: "Binding conflicts with file-level variable",
910 bp: `
911 my_binding = "asdf"
912 my_module_type {
913 name: "foo",
914 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
915 any @ my_binding: "hello",
916 default: "goodbye",
917 })
918 }
919 `,
920 vendorVars: map[string]map[string]string{
921 "my_namespace": {
922 "my_variable": "world!",
923 },
924 },
925 expectedError: "variable already set in inherited scope, previous assignment",
926 },
927 {
928 name: "Binding in combination with file-level variable",
929 bp: `
930 my_var = " there "
931 my_module_type {
932 name: "foo",
933 my_string: select(soong_config_variable("my_namespace", "my_variable"), {
934 any @ my_binding: "hello" + my_var + 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 there world!"),
946 },
947 },
948 {
949 name: "Bindings in subdirectory inherits variable",
950 fs: map[string][]byte{
951 "Android.bp": []byte(`
952my_var = "abcd"
953`),
954 "directoryB/Android.bp": []byte(`
955my_module_type {
956 name: "foo",
957 my_string: select(soong_config_variable("my_namespace", "variable_a"), {
958 any @ my_binding: my_var + my_binding,
959 default: "",
960 }),
961}
962`),
963 },
964 vendorVars: map[string]map[string]string{
965 "my_namespace": {
966 "variable_a": "e",
967 },
968 },
969 provider: selectsTestProvider{
970 my_string: proptools.StringPtr("abcde"),
971 },
972 },
973 {
974 name: "Cannot modify variable after referenced by select",
975 bp: `
976my_var = "foo"
977my_module_type {
978 name: "foo",
979 my_string: select(soong_config_variable("my_namespace", "variable_a"), {
980 "a": my_var,
981 default: "",
982 }),
983}
984my_var += "bar"
985`,
986 vendorVars: map[string]map[string]string{
987 "my_namespace": {
988 "variable_a": "b", // notably not the value that causes my_var to be referenced
989 },
990 },
991 expectedError: `modified variable "my_var" with \+= after referencing`,
992 },
993 {
994 name: "Cannot shadow variable with binding",
995 bp: `
996my_var = "foo"
997my_module_type {
998 name: "foo",
999 my_string: select(soong_config_variable("my_namespace", "variable_a"), {
1000 any @ my_var: my_var,
1001 default: "",
1002 }),
1003}
1004`,
1005 vendorVars: map[string]map[string]string{
1006 "my_namespace": {
1007 "variable_a": "a",
1008 },
1009 },
1010 expectedError: `variable already set in inherited scope, previous assignment:`,
1011 },
Cole Faustb93f7fe2024-08-29 15:36:15 -07001012 {
1013 name: "Basic string list postprocessor",
1014 bp: `
1015my_defaults {
1016 name: "defaults_a",
1017 my_string_list: ["a", "b", "c"],
1018 string_list_postprocessor_add_to_elements: "1",
1019}
1020my_defaults {
1021 name: "defaults_b",
1022 my_string_list: ["d", "e", "f"],
1023 string_list_postprocessor_add_to_elements: "2",
1024}
1025my_module_type {
1026 name: "foo",
1027 defaults: ["defaults_a", "defaults_b"],
1028}
1029`,
1030 provider: selectsTestProvider{
1031 my_string_list: &[]string{"d2", "e2", "f2", "a1", "b1", "c1"},
1032 },
1033 },
Cole Faust5a231bd2024-02-07 09:43:59 -08001034 }
1035
1036 for _, tc := range testCases {
1037 t.Run(tc.name, func(t *testing.T) {
Cole Faust165a05b2024-06-20 18:17:04 -07001038 fs := tc.fs
1039 if fs == nil {
1040 fs = make(MockFS)
1041 }
1042 if tc.bp != "" {
1043 fs["Android.bp"] = []byte(tc.bp)
1044 }
Cole Faust5a231bd2024-02-07 09:43:59 -08001045 fixtures := GroupFixturePreparers(
Cole Faust02dd6e52024-04-03 17:04:57 -07001046 PrepareForTestWithDefaults,
Cole Faust0aa21cc2024-03-20 12:28:03 -07001047 PrepareForTestWithArchMutator,
Cole Faust5f297062024-05-22 14:30:16 -07001048 PrepareForTestWithSoongConfigModuleBuildComponents,
Cole Faust5a231bd2024-02-07 09:43:59 -08001049 FixtureRegisterWithContext(func(ctx RegistrationContext) {
1050 ctx.RegisterModuleType("my_module_type", newSelectsMockModule)
Cole Faust02dd6e52024-04-03 17:04:57 -07001051 ctx.RegisterModuleType("my_defaults", newSelectsMockModuleDefaults)
Cole Faust5a231bd2024-02-07 09:43:59 -08001052 }),
1053 FixtureModifyProductVariables(func(variables FixtureProductVariables) {
1054 variables.VendorVars = tc.vendorVars
Cole Faust46f6e2f2024-06-20 12:57:43 -07001055 variables.VendorVarTypes = tc.vendorVarTypes
Cole Faust5a231bd2024-02-07 09:43:59 -08001056 }),
Cole Faust165a05b2024-06-20 18:17:04 -07001057 FixtureMergeMockFs(fs),
Cole Faust5a231bd2024-02-07 09:43:59 -08001058 )
1059 if tc.expectedError != "" {
1060 fixtures = fixtures.ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(tc.expectedError))
1061 }
Cole Faust165a05b2024-06-20 18:17:04 -07001062 result := fixtures.RunTest(t)
Cole Faust5a231bd2024-02-07 09:43:59 -08001063
1064 if tc.expectedError == "" {
Cole Faust69349462024-04-25 16:02:15 -07001065 if len(tc.providers) == 0 {
1066 tc.providers = map[string]selectsTestProvider{
1067 "foo": tc.provider,
1068 }
1069 }
1070
1071 for moduleName := range tc.providers {
1072 expected := tc.providers[moduleName]
1073 m := result.ModuleForTests(moduleName, "android_arm64_armv8-a")
1074 p, _ := OtherModuleProvider(result.testContext.OtherModuleProviderAdaptor(), m.Module(), selectsTestProviderKey)
1075 if !reflect.DeepEqual(p, expected) {
1076 t.Errorf("Expected:\n %q\ngot:\n %q", expected.String(), p.String())
1077 }
Cole Faust5a231bd2024-02-07 09:43:59 -08001078 }
1079 }
1080 })
1081 }
1082}
1083
1084type selectsTestProvider struct {
Cole Faust60f6bb22024-04-30 13:58:55 -07001085 my_bool *bool
1086 my_string *string
1087 my_string_list *[]string
1088 my_paths *[]string
1089 replacing_string_list *[]string
Cole Faustba483662024-06-17 15:03:33 -07001090 arch_variant_configurable_bool *bool
Cole Faust60f6bb22024-04-30 13:58:55 -07001091 my_nonconfigurable_bool *bool
1092 my_nonconfigurable_string *string
1093 my_nonconfigurable_string_list []string
Cole Faust5a231bd2024-02-07 09:43:59 -08001094}
1095
1096func (p *selectsTestProvider) String() string {
1097 myBoolStr := "nil"
1098 if p.my_bool != nil {
1099 myBoolStr = fmt.Sprintf("%t", *p.my_bool)
1100 }
1101 myStringStr := "nil"
1102 if p.my_string != nil {
1103 myStringStr = *p.my_string
1104 }
Cole Faust60f6bb22024-04-30 13:58:55 -07001105 myNonconfigurableStringStr := "nil"
Cole Faust5f297062024-05-22 14:30:16 -07001106 if p.my_nonconfigurable_string != nil {
Cole Faust60f6bb22024-04-30 13:58:55 -07001107 myNonconfigurableStringStr = *p.my_nonconfigurable_string
1108 }
Cole Faust5a231bd2024-02-07 09:43:59 -08001109 return fmt.Sprintf(`selectsTestProvider {
1110 my_bool: %v,
1111 my_string: %s,
1112 my_string_list: %s,
Cole Faustbdd8aee2024-03-14 14:33:02 -07001113 my_paths: %s,
Cole Faust02dd6e52024-04-03 17:04:57 -07001114 replacing_string_list %s,
Cole Faustba483662024-06-17 15:03:33 -07001115 arch_variant_configurable_bool %v
Cole Faust60f6bb22024-04-30 13:58:55 -07001116 my_nonconfigurable_bool: %v,
1117 my_nonconfigurable_string: %s,
1118 my_nonconfigurable_string_list: %s,
1119}`,
1120 myBoolStr,
1121 myStringStr,
1122 p.my_string_list,
1123 p.my_paths,
1124 p.replacing_string_list,
Cole Faustba483662024-06-17 15:03:33 -07001125 p.arch_variant_configurable_bool,
Cole Faust60f6bb22024-04-30 13:58:55 -07001126 p.my_nonconfigurable_bool,
1127 myNonconfigurableStringStr,
1128 p.my_nonconfigurable_string_list,
1129 )
Cole Faust5a231bd2024-02-07 09:43:59 -08001130}
1131
1132var selectsTestProviderKey = blueprint.NewProvider[selectsTestProvider]()
1133
1134type selectsMockModuleProperties struct {
Cole Faust60f6bb22024-04-30 13:58:55 -07001135 My_bool proptools.Configurable[bool]
1136 My_string proptools.Configurable[string]
1137 My_string_list proptools.Configurable[[]string]
1138 My_paths proptools.Configurable[[]string] `android:"path"`
1139 Replacing_string_list proptools.Configurable[[]string] `android:"replace_instead_of_append,arch_variant"`
Cole Faustba483662024-06-17 15:03:33 -07001140 Arch_variant_configurable_bool proptools.Configurable[bool] `android:"replace_instead_of_append,arch_variant"`
Cole Faust60f6bb22024-04-30 13:58:55 -07001141 My_nonconfigurable_bool *bool
1142 My_nonconfigurable_string *string
1143 My_nonconfigurable_string_list []string
Cole Faust5a231bd2024-02-07 09:43:59 -08001144}
1145
1146type selectsMockModule struct {
1147 ModuleBase
1148 DefaultableModuleBase
1149 properties selectsMockModuleProperties
1150}
1151
Cole Faust749eeaa2024-05-21 14:19:05 -07001152func optionalToPtr[T any](o proptools.ConfigurableOptional[T]) *T {
1153 if o.IsEmpty() {
1154 return nil
1155 }
1156 x := o.Get()
1157 return &x
1158}
1159
Cole Faust5a231bd2024-02-07 09:43:59 -08001160func (p *selectsMockModule) GenerateAndroidBuildActions(ctx ModuleContext) {
Cole Faustfee6fde2024-06-13 15:35:17 -07001161 toAppend := ctx.Config().VendorConfig("selects_test").String("append_to_string_list")
1162 if toAppend != "" {
1163 p.properties.My_string_list.AppendSimpleValue([]string{toAppend})
1164 }
Cole Faustbdd8aee2024-03-14 14:33:02 -07001165 SetProvider(ctx, selectsTestProviderKey, selectsTestProvider{
Cole Faust749eeaa2024-05-21 14:19:05 -07001166 my_bool: optionalToPtr(p.properties.My_bool.Get(ctx)),
1167 my_string: optionalToPtr(p.properties.My_string.Get(ctx)),
1168 my_string_list: optionalToPtr(p.properties.My_string_list.Get(ctx)),
1169 my_paths: optionalToPtr(p.properties.My_paths.Get(ctx)),
1170 replacing_string_list: optionalToPtr(p.properties.Replacing_string_list.Get(ctx)),
Cole Faustba483662024-06-17 15:03:33 -07001171 arch_variant_configurable_bool: optionalToPtr(p.properties.Arch_variant_configurable_bool.Get(ctx)),
Cole Faust60f6bb22024-04-30 13:58:55 -07001172 my_nonconfigurable_bool: p.properties.My_nonconfigurable_bool,
1173 my_nonconfigurable_string: p.properties.My_nonconfigurable_string,
1174 my_nonconfigurable_string_list: p.properties.My_nonconfigurable_string_list,
Cole Faust5a231bd2024-02-07 09:43:59 -08001175 })
1176}
1177
1178func newSelectsMockModule() Module {
1179 m := &selectsMockModule{}
1180 m.AddProperties(&m.properties)
Cole Faust0aa21cc2024-03-20 12:28:03 -07001181 InitAndroidArchModule(m, HostAndDeviceSupported, MultilibFirst)
Cole Faust5a231bd2024-02-07 09:43:59 -08001182 InitDefaultableModule(m)
1183 return m
1184}
Cole Faust02dd6e52024-04-03 17:04:57 -07001185
Cole Faustb93f7fe2024-08-29 15:36:15 -07001186type selectsMockDefaultsProperties struct {
1187 String_list_postprocessor_add_to_elements string
1188}
1189
Cole Faust02dd6e52024-04-03 17:04:57 -07001190type selectsMockModuleDefaults struct {
1191 ModuleBase
1192 DefaultsModuleBase
Cole Faustb93f7fe2024-08-29 15:36:15 -07001193 myProperties selectsMockModuleProperties
1194 defaultsProperties selectsMockDefaultsProperties
Cole Faust02dd6e52024-04-03 17:04:57 -07001195}
1196
1197func (d *selectsMockModuleDefaults) GenerateAndroidBuildActions(ctx ModuleContext) {
1198}
1199
1200func newSelectsMockModuleDefaults() Module {
1201 module := &selectsMockModuleDefaults{}
1202
1203 module.AddProperties(
Cole Faustb93f7fe2024-08-29 15:36:15 -07001204 &module.myProperties,
1205 &module.defaultsProperties,
Cole Faust02dd6e52024-04-03 17:04:57 -07001206 )
1207
1208 InitDefaultsModule(module)
1209
Cole Faustb93f7fe2024-08-29 15:36:15 -07001210 AddLoadHook(module, func(lhc LoadHookContext) {
1211 if module.defaultsProperties.String_list_postprocessor_add_to_elements != "" {
1212 module.myProperties.My_string_list.AddPostProcessor(func(x []string) []string {
1213 for i := range x {
1214 x[i] = x[i] + module.defaultsProperties.String_list_postprocessor_add_to_elements
1215 }
1216 return x
1217 })
1218 }
1219 })
1220
Cole Faust02dd6e52024-04-03 17:04:57 -07001221 return module
1222}