| // Copyright 2024 Google Inc. All rights reserved. | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //     http://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | package android | 
 |  | 
 | import ( | 
 | 	"fmt" | 
 | 	"reflect" | 
 | 	"testing" | 
 |  | 
 | 	"github.com/google/blueprint" | 
 | 	"github.com/google/blueprint/proptools" | 
 | ) | 
 |  | 
 | func TestSelects(t *testing.T) { | 
 | 	testCases := []struct { | 
 | 		name          string | 
 | 		bp            string | 
 | 		provider      selectsTestProvider | 
 | 		vendorVars    map[string]map[string]string | 
 | 		expectedError string | 
 | 	}{ | 
 | 		{ | 
 | 			name: "basic string list", | 
 | 			bp: ` | 
 | 			my_module_type { | 
 | 				name: "foo", | 
 | 				my_string_list: select(soong_config_variable("my_namespace", "my_variable"), { | 
 | 					"a": ["a.cpp"], | 
 | 					"b": ["b.cpp"], | 
 | 					_: ["c.cpp"], | 
 | 				}), | 
 | 			} | 
 | 			`, | 
 | 			provider: selectsTestProvider{ | 
 | 				my_string_list: &[]string{"c.cpp"}, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			name: "basic string", | 
 | 			bp: ` | 
 | 			my_module_type { | 
 | 				name: "foo", | 
 | 				my_string: select(soong_config_variable("my_namespace", "my_variable"), { | 
 | 					"a": "a.cpp", | 
 | 					"b": "b.cpp", | 
 | 					_: "c.cpp", | 
 | 				}), | 
 | 			} | 
 | 			`, | 
 | 			provider: selectsTestProvider{ | 
 | 				my_string: proptools.StringPtr("c.cpp"), | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			name: "basic bool", | 
 | 			bp: ` | 
 | 			my_module_type { | 
 | 				name: "foo", | 
 | 				my_bool: select(soong_config_variable("my_namespace", "my_variable"), { | 
 | 					"a": true, | 
 | 					"b": false, | 
 | 					_: true, | 
 | 				}), | 
 | 			} | 
 | 			`, | 
 | 			provider: selectsTestProvider{ | 
 | 				my_bool: proptools.BoolPtr(true), | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			name: "Differing types", | 
 | 			bp: ` | 
 | 			my_module_type { | 
 | 				name: "foo", | 
 | 				my_string: select(soong_config_variable("my_namespace", "my_variable"), { | 
 | 					"a": "a.cpp", | 
 | 					"b": true, | 
 | 					_: "c.cpp", | 
 | 				}), | 
 | 			} | 
 | 			`, | 
 | 			expectedError: `can't assign bool value to string property "my_string\[1\]"`, | 
 | 		}, | 
 | 		{ | 
 | 			name: "String list non-default", | 
 | 			bp: ` | 
 | 			my_module_type { | 
 | 				name: "foo", | 
 | 				my_string_list: select(soong_config_variable("my_namespace", "my_variable"), { | 
 | 					"a": ["a.cpp"], | 
 | 					"b": ["b.cpp"], | 
 | 					_: ["c.cpp"], | 
 | 				}), | 
 | 			} | 
 | 			`, | 
 | 			provider: selectsTestProvider{ | 
 | 				my_string_list: &[]string{"a.cpp"}, | 
 | 			}, | 
 | 			vendorVars: map[string]map[string]string{ | 
 | 				"my_namespace": { | 
 | 					"my_variable": "a", | 
 | 				}, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			name: "String list append", | 
 | 			bp: ` | 
 | 			my_module_type { | 
 | 				name: "foo", | 
 | 				my_string_list: select(soong_config_variable("my_namespace", "my_variable"), { | 
 | 					"a": ["a.cpp"], | 
 | 					"b": ["b.cpp"], | 
 | 					_: ["c.cpp"], | 
 | 				}) + select(soong_config_variable("my_namespace", "my_variable_2"), { | 
 | 					"a2": ["a2.cpp"], | 
 | 					"b2": ["b2.cpp"], | 
 | 					_: ["c2.cpp"], | 
 | 				}), | 
 | 			} | 
 | 			`, | 
 | 			provider: selectsTestProvider{ | 
 | 				my_string_list: &[]string{"a.cpp", "c2.cpp"}, | 
 | 			}, | 
 | 			vendorVars: map[string]map[string]string{ | 
 | 				"my_namespace": { | 
 | 					"my_variable": "a", | 
 | 				}, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			name: "String list prepend literal", | 
 | 			bp: ` | 
 | 			my_module_type { | 
 | 				name: "foo", | 
 | 				my_string_list: ["literal.cpp"] + select(soong_config_variable("my_namespace", "my_variable"), { | 
 | 					"a2": ["a2.cpp"], | 
 | 					"b2": ["b2.cpp"], | 
 | 					_: ["c2.cpp"], | 
 | 				}), | 
 | 			} | 
 | 			`, | 
 | 			provider: selectsTestProvider{ | 
 | 				my_string_list: &[]string{"literal.cpp", "c2.cpp"}, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			name: "String list append literal", | 
 | 			bp: ` | 
 | 			my_module_type { | 
 | 				name: "foo", | 
 | 				my_string_list: select(soong_config_variable("my_namespace", "my_variable"), { | 
 | 					"a2": ["a2.cpp"], | 
 | 					"b2": ["b2.cpp"], | 
 | 					_: ["c2.cpp"], | 
 | 				}) + ["literal.cpp"], | 
 | 			} | 
 | 			`, | 
 | 			provider: selectsTestProvider{ | 
 | 				my_string_list: &[]string{"c2.cpp", "literal.cpp"}, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			name: "Can't append bools", | 
 | 			bp: ` | 
 | 			my_module_type { | 
 | 				name: "foo", | 
 | 				my_bool: select(soong_config_variable("my_namespace", "my_variable"), { | 
 | 					"a": true, | 
 | 					"b": false, | 
 | 					_: true, | 
 | 				}) + false, | 
 | 			} | 
 | 			`, | 
 | 			expectedError: "my_bool: Cannot append bools", | 
 | 		}, | 
 | 		{ | 
 | 			name: "Append string", | 
 | 			bp: ` | 
 | 			my_module_type { | 
 | 				name: "foo", | 
 | 				my_string: select(soong_config_variable("my_namespace", "my_variable"), { | 
 | 					"a": "a", | 
 | 					"b": "b", | 
 | 					_: "c", | 
 | 				}) + ".cpp", | 
 | 			} | 
 | 			`, | 
 | 			provider: selectsTestProvider{ | 
 | 				my_string: proptools.StringPtr("c.cpp"), | 
 | 			}, | 
 | 		}, | 
 | 	} | 
 |  | 
 | 	for _, tc := range testCases { | 
 | 		t.Run(tc.name, func(t *testing.T) { | 
 | 			fixtures := GroupFixturePreparers( | 
 | 				FixtureRegisterWithContext(func(ctx RegistrationContext) { | 
 | 					ctx.RegisterModuleType("my_module_type", newSelectsMockModule) | 
 | 				}), | 
 | 				FixtureModifyProductVariables(func(variables FixtureProductVariables) { | 
 | 					variables.VendorVars = tc.vendorVars | 
 | 				}), | 
 | 			) | 
 | 			if tc.expectedError != "" { | 
 | 				fixtures = fixtures.ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(tc.expectedError)) | 
 | 			} | 
 | 			result := fixtures.RunTestWithBp(t, tc.bp) | 
 |  | 
 | 			if tc.expectedError == "" { | 
 | 				m := result.ModuleForTests("foo", "") | 
 | 				p, _ := OtherModuleProvider[selectsTestProvider](result.testContext.OtherModuleProviderAdaptor(), m.Module(), selectsTestProviderKey) | 
 | 				if !reflect.DeepEqual(p, tc.provider) { | 
 | 					t.Errorf("Expected:\n  %q\ngot:\n  %q", tc.provider.String(), p.String()) | 
 | 				} | 
 | 			} | 
 | 		}) | 
 | 	} | 
 | } | 
 |  | 
 | type selectsTestProvider struct { | 
 | 	my_bool        *bool | 
 | 	my_string      *string | 
 | 	my_string_list *[]string | 
 | } | 
 |  | 
 | func (p *selectsTestProvider) String() string { | 
 | 	myBoolStr := "nil" | 
 | 	if p.my_bool != nil { | 
 | 		myBoolStr = fmt.Sprintf("%t", *p.my_bool) | 
 | 	} | 
 | 	myStringStr := "nil" | 
 | 	if p.my_string != nil { | 
 | 		myStringStr = *p.my_string | 
 | 	} | 
 | 	return fmt.Sprintf(`selectsTestProvider { | 
 | 	my_bool: %v, | 
 | 	my_string: %s, | 
 |     my_string_list: %s, | 
 | }`, myBoolStr, myStringStr, p.my_string_list) | 
 | } | 
 |  | 
 | var selectsTestProviderKey = blueprint.NewProvider[selectsTestProvider]() | 
 |  | 
 | type selectsMockModuleProperties struct { | 
 | 	My_bool        proptools.Configurable[bool] | 
 | 	My_string      proptools.Configurable[string] | 
 | 	My_string_list proptools.Configurable[[]string] | 
 | } | 
 |  | 
 | type selectsMockModule struct { | 
 | 	ModuleBase | 
 | 	DefaultableModuleBase | 
 | 	properties selectsMockModuleProperties | 
 | } | 
 |  | 
 | func (p *selectsMockModule) GenerateAndroidBuildActions(ctx ModuleContext) { | 
 | 	SetProvider[selectsTestProvider](ctx, selectsTestProviderKey, selectsTestProvider{ | 
 | 		my_bool:        p.properties.My_bool.Evaluate(ctx), | 
 | 		my_string:      p.properties.My_string.Evaluate(ctx), | 
 | 		my_string_list: p.properties.My_string_list.Evaluate(ctx), | 
 | 	}) | 
 | } | 
 |  | 
 | func newSelectsMockModule() Module { | 
 | 	m := &selectsMockModule{} | 
 | 	m.AddProperties(&m.properties) | 
 | 	InitAndroidArchModule(m, HostAndDeviceSupported, MultilibCommon) | 
 | 	InitDefaultableModule(m) | 
 | 	return m | 
 | } |