1// 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 proptools 16 17import ( 18 "regexp" 19 "strings" 20 "testing" 21 22 "github.com/google/blueprint/parser" 23) 24 25type testSymlinkStruct struct { 26 Target string 27 Name string 28} 29 30type testPropStructNested struct { 31 My_string_ptr *string 32} 33 34type testPropStruct struct { 35 My_string string 36 My_configurable_string Configurable[string] 37 My_configurable_string_list Configurable[[]string] 38 My_string_ptr *string 39 My_string_list []string 40 My_struct_list []testSymlinkStruct 41 My_bool bool 42 My_int int 43 My_int64 int64 44 Nested testPropStructNested 45} 46 47type testPropStructOnlyConfigurableStringList struct { 48 My_configurable_string_list Configurable[[]string] 49} 50 51func TestRepack(t *testing.T) { 52 testCases := []struct { 53 name string 54 propStructs []interface{} 55 expectedBp string 56 expectedErr string 57 }{ 58 { 59 name: "Simple prop struct", 60 propStructs: []interface{}{&testPropStruct{ 61 My_string: "foo", 62 My_configurable_string: NewSimpleConfigurable("qux"), 63 My_configurable_string_list: NewSimpleConfigurable([]string{"a", "b", "c"}), 64 My_string_ptr: StringPtr("bar"), 65 My_string_list: []string{"foo", "bar"}, 66 My_struct_list: []testSymlinkStruct{ 67 {Name: "foo", Target: "foo_target"}, 68 {Name: "bar", Target: "bar_target"}, 69 }, 70 My_bool: true, 71 My_int: 5, 72 My_int64: 64, 73 Nested: testPropStructNested{ 74 My_string_ptr: StringPtr("baz"), 75 }, 76 }}, 77 expectedBp: ` 78module { 79 my_string: "foo", 80 my_configurable_string: "qux", 81 my_configurable_string_list: [ 82 "a", 83 "b", 84 "c", 85 ], 86 my_string_ptr: "bar", 87 my_string_list: [ 88 "foo", 89 "bar", 90 ], 91 my_struct_list: [ 92 { 93 Target: "foo_target", 94 Name: "foo", 95 }, 96 { 97 Target: "bar_target", 98 Name: "bar", 99 }, 100 ], 101 my_bool: true, 102 my_int: 5, 103 my_int64: 64, 104 nested: { 105 my_string_ptr: "baz", 106 }, 107}`, 108 }, 109 { 110 name: "Complicated select", 111 propStructs: []interface{}{&testPropStructOnlyConfigurableStringList{ 112 My_configurable_string_list: createComplicatedSelect(), 113 }}, 114 expectedBp: ` 115module { 116 my_configurable_string_list: ["a"] + select((os(), arch()), { 117 ("android", "x86"): [ 118 "android", 119 "x86", 120 ], 121 ("android", "arm64"): [ 122 "android", 123 "arm64", 124 ], 125 (default, "x86"): [ 126 "default", 127 "x86", 128 ], 129 (default, default): [ 130 "default", 131 "default", 132 ], 133 }) + ["b"], 134}`, 135 }, 136 { 137 name: "Multiple property structs", 138 propStructs: []interface{}{ 139 &testPropStruct{ 140 My_string: "foo", 141 My_string_ptr: nil, 142 My_string_list: []string{"foo", "bar"}, 143 My_bool: true, 144 My_int: 5, 145 }, 146 &testPropStructNested{ 147 My_string_ptr: StringPtr("bar"), 148 }, 149 }, 150 expectedBp: ` 151module { 152 my_string: "foo", 153 my_string_ptr: "bar", 154 my_string_list: [ 155 "foo", 156 "bar", 157 ], 158 my_struct_list: [], 159 my_bool: true, 160 my_int: 5, 161 my_int64: 0, 162}`, 163 }, 164 { 165 name: "Multiple conflicting property structs", 166 propStructs: []interface{}{ 167 &testPropStruct{ 168 My_string: "foo", 169 My_string_ptr: StringPtr("foo"), 170 My_string_list: []string{"foo", "bar"}, 171 My_bool: true, 172 My_int: 5, 173 }, 174 &testPropStructNested{ 175 My_string_ptr: StringPtr("bar"), 176 }, 177 }, 178 expectedErr: `Conflicting fields in property structs had values "foo" and "bar"`, 179 }, 180 } 181 182 for _, tc := range testCases { 183 t.Run(tc.name, func(t *testing.T) { 184 result, err := RepackProperties(tc.propStructs) 185 if err != nil { 186 if tc.expectedErr != "" { 187 match, err2 := regexp.MatchString(tc.expectedErr, err.Error()) 188 if err2 != nil { 189 t.Fatal(err2) 190 } 191 if !match { 192 t.Fatalf("Expected error matching %q, found %q", tc.expectedErr, err.Error()) 193 } 194 return 195 } else { 196 t.Fatal(err) 197 } 198 } else if tc.expectedErr != "" { 199 t.Fatalf("Expected error matching %q, but got success", tc.expectedErr) 200 } 201 file := &parser.File{ 202 Defs: []parser.Definition{ 203 &parser.Module{ 204 Type: "module", 205 Map: *result, 206 }, 207 }, 208 } 209 bytes, err := parser.Print(file) 210 if err != nil { 211 t.Fatal(err) 212 } 213 expected := strings.TrimSpace(tc.expectedBp) 214 actual := strings.TrimSpace(string(bytes)) 215 if expected != actual { 216 t.Fatalf("Expected:\n%s\nBut found:\n%s\n", expected, actual) 217 } 218 }) 219 } 220} 221 222func createComplicatedSelect() Configurable[[]string] { 223 result := NewSimpleConfigurable([]string{"a"}) 224 result.Append(NewConfigurable([]ConfigurableCondition{ 225 NewConfigurableCondition("os", nil), 226 NewConfigurableCondition("arch", nil), 227 }, []ConfigurableCase[[]string]{ 228 NewConfigurableCase([]ConfigurablePattern{ 229 NewStringConfigurablePattern("android"), 230 NewStringConfigurablePattern("x86"), 231 }, &[]string{"android", "x86"}), 232 NewConfigurableCase([]ConfigurablePattern{ 233 NewStringConfigurablePattern("android"), 234 NewStringConfigurablePattern("arm64"), 235 }, &[]string{"android", "arm64"}), 236 NewConfigurableCase([]ConfigurablePattern{ 237 NewDefaultConfigurablePattern(), 238 NewStringConfigurablePattern("x86"), 239 }, &[]string{"default", "x86"}), 240 NewConfigurableCase([]ConfigurablePattern{ 241 NewDefaultConfigurablePattern(), 242 NewDefaultConfigurablePattern(), 243 }, &[]string{"default", "default"}), 244 })) 245 result.Append(NewSimpleConfigurable([]string{"b"})) 246 return result 247} 248