| // Copyright 2020 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 soongconfig |
| |
| import ( |
| "reflect" |
| "testing" |
| |
| "github.com/google/blueprint/proptools" |
| ) |
| |
| func Test_CanonicalizeToProperty(t *testing.T) { |
| tests := []struct { |
| name string |
| arg string |
| want string |
| }{ |
| { |
| name: "lowercase", |
| arg: "board", |
| want: "board", |
| }, |
| { |
| name: "uppercase", |
| arg: "BOARD", |
| want: "BOARD", |
| }, |
| { |
| name: "numbers", |
| arg: "BOARD123", |
| want: "BOARD123", |
| }, |
| { |
| name: "underscore", |
| arg: "TARGET_BOARD", |
| want: "TARGET_BOARD", |
| }, |
| { |
| name: "dash", |
| arg: "TARGET-BOARD", |
| want: "TARGET_BOARD", |
| }, |
| { |
| name: "unicode", |
| arg: "boardĪ»", |
| want: "board_", |
| }, |
| } |
| for _, tt := range tests { |
| t.Run(tt.name, func(t *testing.T) { |
| if got := CanonicalizeToProperty(tt.arg); got != tt.want { |
| t.Errorf("canonicalizeToProperty() = %v, want %v", got, tt.want) |
| } |
| }) |
| } |
| } |
| |
| func Test_typeForPropertyFromPropertyStruct(t *testing.T) { |
| tests := []struct { |
| name string |
| ps interface{} |
| property string |
| want string |
| }{ |
| { |
| name: "string", |
| ps: struct { |
| A string |
| }{}, |
| property: "a", |
| want: "string", |
| }, |
| { |
| name: "list", |
| ps: struct { |
| A []string |
| }{}, |
| property: "a", |
| want: "[]string", |
| }, |
| { |
| name: "missing", |
| ps: struct { |
| A []string |
| }{}, |
| property: "b", |
| want: "", |
| }, |
| { |
| name: "nested", |
| ps: struct { |
| A struct { |
| B string |
| } |
| }{}, |
| property: "a.b", |
| want: "string", |
| }, |
| { |
| name: "missing nested", |
| ps: struct { |
| A struct { |
| B string |
| } |
| }{}, |
| property: "a.c", |
| want: "", |
| }, |
| { |
| name: "not a struct", |
| ps: struct { |
| A string |
| }{}, |
| property: "a.b", |
| want: "", |
| }, |
| { |
| name: "nested pointer", |
| ps: struct { |
| A *struct { |
| B string |
| } |
| }{}, |
| property: "a.b", |
| want: "string", |
| }, |
| { |
| name: "nested interface", |
| ps: struct { |
| A interface{} |
| }{ |
| A: struct { |
| B string |
| }{}, |
| }, |
| property: "a.b", |
| want: "string", |
| }, |
| { |
| name: "nested interface pointer", |
| ps: struct { |
| A interface{} |
| }{ |
| A: &struct { |
| B string |
| }{}, |
| }, |
| property: "a.b", |
| want: "string", |
| }, |
| { |
| name: "nested interface nil pointer", |
| ps: struct { |
| A interface{} |
| }{ |
| A: (*struct { |
| B string |
| })(nil), |
| }, |
| property: "a.b", |
| want: "string", |
| }, |
| } |
| for _, tt := range tests { |
| t.Run(tt.name, func(t *testing.T) { |
| typ := typeForPropertyFromPropertyStruct(tt.ps, tt.property) |
| got := "" |
| if typ != nil { |
| got = typ.String() |
| } |
| if got != tt.want { |
| t.Errorf("typeForPropertyFromPropertyStruct() = %v, want %v", got, tt.want) |
| } |
| }) |
| } |
| } |
| |
| func Test_createAffectablePropertiesType(t *testing.T) { |
| tests := []struct { |
| name string |
| affectableProperties []string |
| factoryProps interface{} |
| want string |
| }{ |
| { |
| name: "string", |
| affectableProperties: []string{"cflags"}, |
| factoryProps: struct { |
| Cflags string |
| }{}, |
| want: "*struct { Cflags string }", |
| }, |
| { |
| name: "list", |
| affectableProperties: []string{"cflags"}, |
| factoryProps: struct { |
| Cflags []string |
| }{}, |
| want: "*struct { Cflags []string }", |
| }, |
| { |
| name: "string pointer", |
| affectableProperties: []string{"cflags"}, |
| factoryProps: struct { |
| Cflags *string |
| }{}, |
| want: "*struct { Cflags *string }", |
| }, |
| { |
| name: "subset", |
| affectableProperties: []string{"cflags"}, |
| factoryProps: struct { |
| Cflags string |
| Ldflags string |
| }{}, |
| want: "*struct { Cflags string }", |
| }, |
| { |
| name: "none", |
| affectableProperties: []string{"cflags"}, |
| factoryProps: struct { |
| Ldflags string |
| }{}, |
| want: "", |
| }, |
| { |
| name: "nested", |
| affectableProperties: []string{"multilib.lib32.cflags"}, |
| factoryProps: struct { |
| Multilib struct { |
| Lib32 struct { |
| Cflags string |
| } |
| } |
| }{}, |
| want: "*struct { Multilib struct { Lib32 struct { Cflags string } } }", |
| }, |
| { |
| name: "complex", |
| affectableProperties: []string{ |
| "cflags", |
| "multilib.lib32.cflags", |
| "multilib.lib32.ldflags", |
| "multilib.lib64.cflags", |
| "multilib.lib64.ldflags", |
| "zflags", |
| }, |
| factoryProps: struct { |
| Cflags string |
| Multilib struct { |
| Lib32 struct { |
| Cflags string |
| Ldflags string |
| } |
| Lib64 struct { |
| Cflags string |
| Ldflags string |
| } |
| } |
| Zflags string |
| }{}, |
| want: "*struct { Cflags string; Multilib struct { Lib32 struct { Cflags string; Ldflags string }; Lib64 struct { Cflags string; Ldflags string } }; Zflags string }", |
| }, |
| } |
| for _, tt := range tests { |
| t.Run(tt.name, func(t *testing.T) { |
| typ := createAffectablePropertiesType(tt.affectableProperties, []interface{}{tt.factoryProps}) |
| got := "" |
| if typ != nil { |
| got = typ.String() |
| } |
| if !reflect.DeepEqual(got, tt.want) { |
| t.Errorf("createAffectablePropertiesType() = %v, want %v", got, tt.want) |
| } |
| }) |
| } |
| } |
| |
| type properties struct { |
| A *string |
| B bool |
| } |
| |
| type boolVarProps struct { |
| A *string |
| B bool |
| Conditions_default *properties |
| } |
| |
| type soongConfigVars struct { |
| Bool_var interface{} |
| } |
| |
| type stringSoongConfigVars struct { |
| String_var interface{} |
| } |
| |
| func Test_PropertiesToApply(t *testing.T) { |
| mt, _ := newModuleType(&ModuleTypeProperties{ |
| Module_type: "foo", |
| Config_namespace: "bar", |
| Bool_variables: []string{"bool_var"}, |
| Properties: []string{"a", "b"}, |
| }) |
| boolVarPositive := &properties{ |
| A: proptools.StringPtr("A"), |
| B: true, |
| } |
| conditionsDefault := &properties{ |
| A: proptools.StringPtr("default"), |
| B: false, |
| } |
| actualProps := &struct { |
| Soong_config_variables soongConfigVars |
| }{ |
| Soong_config_variables: soongConfigVars{ |
| Bool_var: &boolVarProps{ |
| A: boolVarPositive.A, |
| B: boolVarPositive.B, |
| Conditions_default: conditionsDefault, |
| }, |
| }, |
| } |
| props := reflect.ValueOf(actualProps) |
| |
| testCases := []struct { |
| name string |
| config SoongConfig |
| wantProps []interface{} |
| }{ |
| { |
| name: "no_vendor_config", |
| config: Config(map[string]string{}), |
| wantProps: []interface{}{conditionsDefault}, |
| }, |
| { |
| name: "vendor_config_false", |
| config: Config(map[string]string{"bool_var": "n"}), |
| wantProps: []interface{}{conditionsDefault}, |
| }, |
| { |
| name: "bool_var_true", |
| config: Config(map[string]string{"bool_var": "y"}), |
| wantProps: []interface{}{boolVarPositive}, |
| }, |
| } |
| |
| for _, tc := range testCases { |
| gotProps, err := PropertiesToApply(mt, props, tc.config) |
| if err != nil { |
| t.Errorf("%s: Unexpected error in PropertiesToApply: %s", tc.name, err) |
| } |
| |
| if !reflect.DeepEqual(gotProps, tc.wantProps) { |
| t.Errorf("%s: Expected %s, got %s", tc.name, tc.wantProps, gotProps) |
| } |
| } |
| } |
| |
| func Test_PropertiesToApply_String_Error(t *testing.T) { |
| mt, _ := newModuleType(&ModuleTypeProperties{ |
| Module_type: "foo", |
| Config_namespace: "bar", |
| Variables: []string{"string_var"}, |
| Properties: []string{"a", "b"}, |
| }) |
| mt.Variables = append(mt.Variables, &stringVariable{ |
| baseVariable: baseVariable{ |
| variable: "string_var", |
| }, |
| values: []string{"a", "b", "c"}, |
| }) |
| stringVarPositive := &properties{ |
| A: proptools.StringPtr("A"), |
| B: true, |
| } |
| conditionsDefault := &properties{ |
| A: proptools.StringPtr("default"), |
| B: false, |
| } |
| actualProps := &struct { |
| Soong_config_variables stringSoongConfigVars |
| }{ |
| Soong_config_variables: stringSoongConfigVars{ |
| String_var: &boolVarProps{ |
| A: stringVarPositive.A, |
| B: stringVarPositive.B, |
| Conditions_default: conditionsDefault, |
| }, |
| }, |
| } |
| props := reflect.ValueOf(actualProps) |
| |
| _, err := PropertiesToApply(mt, props, Config(map[string]string{ |
| "string_var": "x", |
| })) |
| expected := `Soong config property "string_var" must be one of [a b c], found "x"` |
| if err == nil { |
| t.Fatalf("Expected an error, got nil") |
| } else if err.Error() != expected { |
| t.Fatalf("Error message was not correct, expected %q, got %q", expected, err.Error()) |
| } |
| } |
| |
| func Test_Bp2BuildSoongConfigDefinitionsAddVars(t *testing.T) { |
| testCases := []struct { |
| desc string |
| defs []*SoongConfigDefinition |
| expected Bp2BuildSoongConfigDefinitions |
| }{ |
| { |
| desc: "non-overlapping", |
| defs: []*SoongConfigDefinition{ |
| &SoongConfigDefinition{ |
| ModuleTypes: map[string]*ModuleType{ |
| "a": &ModuleType{ |
| ConfigNamespace: "foo", |
| Variables: []soongConfigVariable{ |
| &stringVariable{ |
| baseVariable: baseVariable{"string_var"}, |
| values: []string{"a", "b", "c"}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| &SoongConfigDefinition{ |
| ModuleTypes: map[string]*ModuleType{ |
| "b": &ModuleType{ |
| ConfigNamespace: "foo", |
| Variables: []soongConfigVariable{ |
| &stringVariable{ |
| baseVariable: baseVariable{"string_var"}, |
| values: []string{"a", "b", "c"}, |
| }, |
| &boolVariable{baseVariable: baseVariable{"bool_var"}}, |
| &valueVariable{baseVariable: baseVariable{"variable_var"}}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| expected: Bp2BuildSoongConfigDefinitions{ |
| StringVars: map[string]map[string]bool{ |
| "foo__string_var": map[string]bool{"a": true, "b": true, "c": true}, |
| }, |
| BoolVars: map[string]bool{"foo__bool_var": true}, |
| ValueVars: map[string]bool{"foo__variable_var": true}, |
| }, |
| }, |
| { |
| desc: "overlapping", |
| defs: []*SoongConfigDefinition{ |
| &SoongConfigDefinition{ |
| ModuleTypes: map[string]*ModuleType{ |
| "a": &ModuleType{ |
| ConfigNamespace: "foo", |
| Variables: []soongConfigVariable{ |
| &stringVariable{ |
| baseVariable: baseVariable{"string_var"}, |
| values: []string{"a", "b", "c"}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| &SoongConfigDefinition{ |
| ModuleTypes: map[string]*ModuleType{ |
| "b": &ModuleType{ |
| ConfigNamespace: "foo", |
| Variables: []soongConfigVariable{ |
| &stringVariable{ |
| baseVariable: baseVariable{"string_var"}, |
| values: []string{"b", "c", "d"}, |
| }, |
| &boolVariable{baseVariable: baseVariable{"bool_var"}}, |
| &valueVariable{baseVariable: baseVariable{"variable_var"}}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| expected: Bp2BuildSoongConfigDefinitions{ |
| StringVars: map[string]map[string]bool{ |
| "foo__string_var": map[string]bool{"a": true, "b": true, "c": true, "d": true}, |
| }, |
| BoolVars: map[string]bool{"foo__bool_var": true}, |
| ValueVars: map[string]bool{"foo__variable_var": true}, |
| }, |
| }, |
| } |
| |
| for _, tc := range testCases { |
| t.Run(tc.desc, func(t *testing.T) { |
| actual := &Bp2BuildSoongConfigDefinitions{} |
| for _, d := range tc.defs { |
| func(def *SoongConfigDefinition) { |
| actual.AddVars(def) |
| }(d) |
| } |
| if !reflect.DeepEqual(*actual, tc.expected) { |
| t.Errorf("Expected %#v, got %#v", tc.expected, *actual) |
| } |
| }) |
| } |
| |
| } |
| |
| func Test_Bp2BuildSoongConfigDefinitions(t *testing.T) { |
| testCases := []struct { |
| desc string |
| defs Bp2BuildSoongConfigDefinitions |
| expected string |
| }{ |
| { |
| desc: "all empty", |
| defs: Bp2BuildSoongConfigDefinitions{}, |
| expected: `soong_config_bool_variables = {} |
| |
| soong_config_value_variables = {} |
| |
| soong_config_string_variables = {}`}, { |
| desc: "only bool", |
| defs: Bp2BuildSoongConfigDefinitions{ |
| BoolVars: map[string]bool{ |
| "bool_var": true, |
| }, |
| }, |
| expected: `soong_config_bool_variables = { |
| "bool_var": True, |
| } |
| |
| soong_config_value_variables = {} |
| |
| soong_config_string_variables = {}`}, { |
| desc: "only value vars", |
| defs: Bp2BuildSoongConfigDefinitions{ |
| ValueVars: map[string]bool{ |
| "value_var": true, |
| }, |
| }, |
| expected: `soong_config_bool_variables = {} |
| |
| soong_config_value_variables = { |
| "value_var": True, |
| } |
| |
| soong_config_string_variables = {}`}, { |
| desc: "only string vars", |
| defs: Bp2BuildSoongConfigDefinitions{ |
| StringVars: map[string]map[string]bool{ |
| "string_var": map[string]bool{ |
| "choice1": true, |
| "choice2": true, |
| "choice3": true, |
| }, |
| }, |
| }, |
| expected: `soong_config_bool_variables = {} |
| |
| soong_config_value_variables = {} |
| |
| soong_config_string_variables = { |
| "string_var": [ |
| "choice1", |
| "choice2", |
| "choice3", |
| ], |
| }`}, { |
| desc: "all vars", |
| defs: Bp2BuildSoongConfigDefinitions{ |
| BoolVars: map[string]bool{ |
| "bool_var_one": true, |
| }, |
| ValueVars: map[string]bool{ |
| "value_var_one": true, |
| "value_var_two": true, |
| }, |
| StringVars: map[string]map[string]bool{ |
| "string_var_one": map[string]bool{ |
| "choice1": true, |
| "choice2": true, |
| "choice3": true, |
| }, |
| "string_var_two": map[string]bool{ |
| "foo": true, |
| "bar": true, |
| }, |
| }, |
| }, |
| expected: `soong_config_bool_variables = { |
| "bool_var_one": True, |
| } |
| |
| soong_config_value_variables = { |
| "value_var_one": True, |
| "value_var_two": True, |
| } |
| |
| soong_config_string_variables = { |
| "string_var_one": [ |
| "choice1", |
| "choice2", |
| "choice3", |
| ], |
| "string_var_two": [ |
| "bar", |
| "foo", |
| ], |
| }`}, |
| } |
| for _, test := range testCases { |
| t.Run(test.desc, func(t *testing.T) { |
| actual := test.defs.String() |
| if actual != test.expected { |
| t.Errorf("Expected:\n%s\nbut got:\n%s", test.expected, actual) |
| } |
| }) |
| } |
| } |