1 // Copyright 2014 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 15 package blueprint 16 17 import ( 18 "bytes" 19 "fmt" 20 "reflect" 21 "testing" 22 "text/scanner" 23 24 "github.com/google/blueprint/parser" 25 "github.com/google/blueprint/proptools" 26 ) 27 28 var validUnpackTestCases = []struct { 29 input string 30 output interface{} 31 errs []error 32 }{ 33 {` 34 m { 35 name: "abc", 36 blank: "", 37 } 38 `, 39 struct { 40 Name *string 41 Blank *string 42 Unset *string 43 }{ 44 Name: proptools.StringPtr("abc"), 45 Blank: proptools.StringPtr(""), 46 Unset: nil, 47 }, 48 nil, 49 }, 50 51 {` 52 m { 53 name: "abc", 54 } 55 `, 56 struct { 57 Name string 58 }{ 59 Name: "abc", 60 }, 61 nil, 62 }, 63 64 {` 65 m { 66 isGood: true, 67 } 68 `, 69 struct { 70 IsGood bool 71 }{ 72 IsGood: true, 73 }, 74 nil, 75 }, 76 77 {` 78 m { 79 isGood: true, 80 isBad: false, 81 } 82 `, 83 struct { 84 IsGood *bool 85 IsBad *bool 86 IsUgly *bool 87 }{ 88 IsGood: proptools.BoolPtr(true), 89 IsBad: proptools.BoolPtr(false), 90 IsUgly: nil, 91 }, 92 nil, 93 }, 94 95 {` 96 m { 97 stuff: ["asdf", "jkl;", "qwert", 98 "uiop", "bnm,"], 99 empty: [] 100 } 101 `, 102 struct { 103 Stuff []string 104 Empty []string 105 Nil []string 106 }{ 107 Stuff: []string{"asdf", "jkl;", "qwert", "uiop", "bnm,"}, 108 Empty: []string{}, 109 Nil: nil, 110 }, 111 nil, 112 }, 113 114 {` 115 m { 116 nested: { 117 name: "abc", 118 } 119 } 120 `, 121 struct { 122 Nested struct { 123 Name string 124 } 125 }{ 126 Nested: struct{ Name string }{ 127 Name: "abc", 128 }, 129 }, 130 nil, 131 }, 132 133 {` 134 m { 135 nested: { 136 name: "def", 137 } 138 } 139 `, 140 struct { 141 Nested interface{} 142 }{ 143 Nested: &struct{ Name string }{ 144 Name: "def", 145 }, 146 }, 147 nil, 148 }, 149 150 {` 151 m { 152 nested: { 153 foo: "abc", 154 }, 155 bar: false, 156 baz: ["def", "ghi"], 157 } 158 `, 159 struct { 160 Nested struct { 161 Foo string 162 } 163 Bar bool 164 Baz []string 165 }{ 166 Nested: struct{ Foo string }{ 167 Foo: "abc", 168 }, 169 Bar: false, 170 Baz: []string{"def", "ghi"}, 171 }, 172 nil, 173 }, 174 175 {` 176 m { 177 nested: { 178 foo: "abc", 179 }, 180 bar: false, 181 baz: ["def", "ghi"], 182 } 183 `, 184 struct { 185 Nested struct { 186 Foo string `allowNested:"true"` 187 } `blueprint:"filter(allowNested:\"true\")"` 188 Bar bool 189 Baz []string 190 }{ 191 Nested: struct { 192 Foo string `allowNested:"true"` 193 }{ 194 Foo: "abc", 195 }, 196 Bar: false, 197 Baz: []string{"def", "ghi"}, 198 }, 199 nil, 200 }, 201 202 {` 203 m { 204 nested: { 205 foo: "abc", 206 }, 207 bar: false, 208 baz: ["def", "ghi"], 209 } 210 `, 211 struct { 212 Nested struct { 213 Foo string 214 } `blueprint:"filter(allowNested:\"true\")"` 215 Bar bool 216 Baz []string 217 }{ 218 Nested: struct{ Foo string }{ 219 Foo: "", 220 }, 221 Bar: false, 222 Baz: []string{"def", "ghi"}, 223 }, 224 []error{ 225 &Error{ 226 Err: fmt.Errorf("filtered field nested.foo cannot be set in a Blueprint file"), 227 Pos: scanner.Position{"", 27, 4, 8}, 228 }, 229 }, 230 }, 231 232 // Anonymous struct 233 {` 234 m { 235 name: "abc", 236 nested: { 237 name: "def", 238 }, 239 } 240 `, 241 struct { 242 EmbeddedStruct 243 Nested struct { 244 EmbeddedStruct 245 } 246 }{ 247 EmbeddedStruct: EmbeddedStruct{ 248 Name: "abc", 249 }, 250 Nested: struct { 251 EmbeddedStruct 252 }{ 253 EmbeddedStruct: EmbeddedStruct{ 254 Name: "def", 255 }, 256 }, 257 }, 258 nil, 259 }, 260 261 // Anonymous interface 262 {` 263 m { 264 name: "abc", 265 nested: { 266 name: "def", 267 }, 268 } 269 `, 270 struct { 271 EmbeddedInterface 272 Nested struct { 273 EmbeddedInterface 274 } 275 }{ 276 EmbeddedInterface: &struct{ Name string }{ 277 Name: "abc", 278 }, 279 Nested: struct { 280 EmbeddedInterface 281 }{ 282 EmbeddedInterface: &struct{ Name string }{ 283 Name: "def", 284 }, 285 }, 286 }, 287 nil, 288 }, 289 290 // Anonymous struct with name collision 291 {` 292 m { 293 name: "abc", 294 nested: { 295 name: "def", 296 }, 297 } 298 `, 299 struct { 300 Name string 301 EmbeddedStruct 302 Nested struct { 303 Name string 304 EmbeddedStruct 305 } 306 }{ 307 Name: "abc", 308 EmbeddedStruct: EmbeddedStruct{ 309 Name: "abc", 310 }, 311 Nested: struct { 312 Name string 313 EmbeddedStruct 314 }{ 315 Name: "def", 316 EmbeddedStruct: EmbeddedStruct{ 317 Name: "def", 318 }, 319 }, 320 }, 321 nil, 322 }, 323 324 // Anonymous interface with name collision 325 {` 326 m { 327 name: "abc", 328 nested: { 329 name: "def", 330 }, 331 } 332 `, 333 struct { 334 Name string 335 EmbeddedInterface 336 Nested struct { 337 Name string 338 EmbeddedInterface 339 } 340 }{ 341 Name: "abc", 342 EmbeddedInterface: &struct{ Name string }{ 343 Name: "abc", 344 }, 345 Nested: struct { 346 Name string 347 EmbeddedInterface 348 }{ 349 Name: "def", 350 EmbeddedInterface: &struct{ Name string }{ 351 Name: "def", 352 }, 353 }, 354 }, 355 nil, 356 }, 357 } 358 359 type EmbeddedStruct struct{ Name string } 360 type EmbeddedInterface interface{} 361 362 func TestUnpackProperties(t *testing.T) { 363 for _, testCase := range validUnpackTestCases { 364 r := bytes.NewBufferString(testCase.input) 365 file, errs := parser.Parse("", r, nil) 366 if len(errs) != 0 { 367 t.Errorf("test case: %s", testCase.input) 368 t.Errorf("unexpected parse errors:") 369 for _, err := range errs { 370 t.Errorf(" %s", err) 371 } 372 t.FailNow() 373 } 374 375 module := file.Defs[0].(*parser.Module) 376 properties := proptools.CloneProperties(reflect.ValueOf(testCase.output)) 377 proptools.ZeroProperties(properties.Elem()) 378 _, errs = unpackProperties(module.Properties, properties.Interface()) 379 if len(errs) != 0 && len(testCase.errs) == 0 { 380 t.Errorf("test case: %s", testCase.input) 381 t.Errorf("unexpected unpack errors:") 382 for _, err := range errs { 383 t.Errorf(" %s", err) 384 } 385 t.FailNow() 386 } else if !reflect.DeepEqual(errs, testCase.errs) { 387 t.Errorf("test case: %s", testCase.input) 388 t.Errorf("incorrect errors:") 389 t.Errorf(" expected: %+v", testCase.errs) 390 t.Errorf(" got: %+v", errs) 391 } 392 393 output := properties.Elem().Interface() 394 if !reflect.DeepEqual(output, testCase.output) { 395 t.Errorf("test case: %s", testCase.input) 396 t.Errorf("incorrect output:") 397 t.Errorf(" expected: %+v", testCase.output) 398 t.Errorf(" got: %+v", output) 399 } 400 } 401 } 402