github.com/westcoastroms/westcoastroms-build@v0.0.0-20190928114312-2350e5a73030/build/blueprint/unpack_test.go (about) 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 empty []interface{} 32 errs []error 33 }{ 34 { 35 input: ` 36 m { 37 name: "abc", 38 blank: "", 39 } 40 `, 41 output: []interface{}{ 42 struct { 43 Name *string 44 Blank *string 45 Unset *string 46 }{ 47 Name: proptools.StringPtr("abc"), 48 Blank: proptools.StringPtr(""), 49 Unset: nil, 50 }, 51 }, 52 }, 53 54 { 55 input: ` 56 m { 57 name: "abc", 58 } 59 `, 60 output: []interface{}{ 61 struct { 62 Name string 63 }{ 64 Name: "abc", 65 }, 66 }, 67 }, 68 69 { 70 input: ` 71 m { 72 isGood: true, 73 } 74 `, 75 output: []interface{}{ 76 struct { 77 IsGood bool 78 }{ 79 IsGood: true, 80 }, 81 }, 82 }, 83 84 { 85 input: ` 86 m { 87 isGood: true, 88 isBad: false, 89 } 90 `, 91 output: []interface{}{ 92 struct { 93 IsGood *bool 94 IsBad *bool 95 IsUgly *bool 96 }{ 97 IsGood: proptools.BoolPtr(true), 98 IsBad: proptools.BoolPtr(false), 99 IsUgly: nil, 100 }, 101 }, 102 }, 103 104 { 105 input: ` 106 m { 107 stuff: ["asdf", "jkl;", "qwert", 108 "uiop", "bnm,"], 109 empty: [] 110 } 111 `, 112 output: []interface{}{ 113 struct { 114 Stuff []string 115 Empty []string 116 Nil []string 117 }{ 118 Stuff: []string{"asdf", "jkl;", "qwert", "uiop", "bnm,"}, 119 Empty: []string{}, 120 Nil: nil, 121 }, 122 }, 123 }, 124 125 { 126 input: ` 127 m { 128 nested: { 129 name: "abc", 130 } 131 } 132 `, 133 output: []interface{}{ 134 struct { 135 Nested struct { 136 Name string 137 } 138 }{ 139 Nested: struct{ Name string }{ 140 Name: "abc", 141 }, 142 }, 143 }, 144 }, 145 146 { 147 input: ` 148 m { 149 nested: { 150 name: "def", 151 } 152 } 153 `, 154 output: []interface{}{ 155 struct { 156 Nested interface{} 157 }{ 158 Nested: &struct{ Name string }{ 159 Name: "def", 160 }, 161 }, 162 }, 163 }, 164 165 { 166 input: ` 167 m { 168 nested: { 169 foo: "abc", 170 }, 171 bar: false, 172 baz: ["def", "ghi"], 173 } 174 `, 175 output: []interface{}{ 176 struct { 177 Nested struct { 178 Foo string 179 } 180 Bar bool 181 Baz []string 182 }{ 183 Nested: struct{ Foo string }{ 184 Foo: "abc", 185 }, 186 Bar: false, 187 Baz: []string{"def", "ghi"}, 188 }, 189 }, 190 }, 191 192 { 193 input: ` 194 m { 195 nested: { 196 foo: "abc", 197 }, 198 bar: false, 199 baz: ["def", "ghi"], 200 } 201 `, 202 output: []interface{}{ 203 struct { 204 Nested struct { 205 Foo string `allowNested:"true"` 206 } `blueprint:"filter(allowNested:\"true\")"` 207 Bar bool 208 Baz []string 209 }{ 210 Nested: struct { 211 Foo string `allowNested:"true"` 212 }{ 213 Foo: "abc", 214 }, 215 Bar: false, 216 Baz: []string{"def", "ghi"}, 217 }, 218 }, 219 }, 220 221 { 222 input: ` 223 m { 224 nested: { 225 foo: "abc", 226 }, 227 bar: false, 228 baz: ["def", "ghi"], 229 } 230 `, 231 output: []interface{}{ 232 struct { 233 Nested struct { 234 Foo string 235 } `blueprint:"filter(allowNested:\"true\")"` 236 Bar bool 237 Baz []string 238 }{ 239 Nested: struct{ Foo string }{ 240 Foo: "", 241 }, 242 Bar: false, 243 Baz: []string{"def", "ghi"}, 244 }, 245 }, 246 errs: []error{ 247 &BlueprintError{ 248 Err: fmt.Errorf("filtered field nested.foo cannot be set in a Blueprint file"), 249 Pos: mkpos(30, 4, 9), 250 }, 251 }, 252 }, 253 254 // Anonymous struct 255 { 256 input: ` 257 m { 258 name: "abc", 259 nested: { 260 name: "def", 261 }, 262 } 263 `, 264 output: []interface{}{ 265 struct { 266 EmbeddedStruct 267 Nested struct { 268 EmbeddedStruct 269 } 270 }{ 271 EmbeddedStruct: EmbeddedStruct{ 272 Name: "abc", 273 }, 274 Nested: struct { 275 EmbeddedStruct 276 }{ 277 EmbeddedStruct: EmbeddedStruct{ 278 Name: "def", 279 }, 280 }, 281 }, 282 }, 283 }, 284 285 // Anonymous interface 286 { 287 input: ` 288 m { 289 name: "abc", 290 nested: { 291 name: "def", 292 }, 293 } 294 `, 295 output: []interface{}{ 296 struct { 297 EmbeddedInterface 298 Nested struct { 299 EmbeddedInterface 300 } 301 }{ 302 EmbeddedInterface: &struct{ Name string }{ 303 Name: "abc", 304 }, 305 Nested: struct { 306 EmbeddedInterface 307 }{ 308 EmbeddedInterface: &struct{ Name string }{ 309 Name: "def", 310 }, 311 }, 312 }, 313 }, 314 }, 315 316 // Anonymous struct with name collision 317 { 318 input: ` 319 m { 320 name: "abc", 321 nested: { 322 name: "def", 323 }, 324 } 325 `, 326 output: []interface{}{ 327 struct { 328 Name string 329 EmbeddedStruct 330 Nested struct { 331 Name string 332 EmbeddedStruct 333 } 334 }{ 335 Name: "abc", 336 EmbeddedStruct: EmbeddedStruct{ 337 Name: "abc", 338 }, 339 Nested: struct { 340 Name string 341 EmbeddedStruct 342 }{ 343 Name: "def", 344 EmbeddedStruct: EmbeddedStruct{ 345 Name: "def", 346 }, 347 }, 348 }, 349 }, 350 }, 351 352 // Anonymous interface with name collision 353 { 354 input: ` 355 m { 356 name: "abc", 357 nested: { 358 name: "def", 359 }, 360 } 361 `, 362 output: []interface{}{ 363 struct { 364 Name string 365 EmbeddedInterface 366 Nested struct { 367 Name string 368 EmbeddedInterface 369 } 370 }{ 371 Name: "abc", 372 EmbeddedInterface: &struct{ Name string }{ 373 Name: "abc", 374 }, 375 Nested: struct { 376 Name string 377 EmbeddedInterface 378 }{ 379 Name: "def", 380 EmbeddedInterface: &struct{ Name string }{ 381 Name: "def", 382 }, 383 }, 384 }, 385 }, 386 }, 387 388 // Variables 389 { 390 input: ` 391 list = ["abc"] 392 string = "def" 393 list_with_variable = [string] 394 m { 395 name: string, 396 list: list, 397 list2: list_with_variable, 398 } 399 `, 400 output: []interface{}{ 401 struct { 402 Name string 403 List []string 404 List2 []string 405 }{ 406 Name: "def", 407 List: []string{"abc"}, 408 List2: []string{"def"}, 409 }, 410 }, 411 }, 412 413 // Multiple property structs 414 { 415 input: ` 416 m { 417 nested: { 418 name: "abc", 419 } 420 } 421 `, 422 output: []interface{}{ 423 struct { 424 Nested struct { 425 Name string 426 } 427 }{ 428 Nested: struct{ Name string }{ 429 Name: "abc", 430 }, 431 }, 432 struct { 433 Nested struct { 434 Name string 435 } 436 }{ 437 Nested: struct{ Name string }{ 438 Name: "abc", 439 }, 440 }, 441 struct { 442 }{}, 443 }, 444 }, 445 446 // Nil pointer to struct 447 { 448 input: ` 449 m { 450 nested: { 451 name: "abc", 452 } 453 } 454 `, 455 output: []interface{}{ 456 struct { 457 Nested *struct { 458 Name string 459 } 460 }{ 461 Nested: &struct{ Name string }{ 462 Name: "abc", 463 }, 464 }, 465 }, 466 empty: []interface{}{ 467 &struct { 468 Nested *struct { 469 Name string 470 } 471 }{}, 472 }, 473 }, 474 475 // Interface containing nil pointer to struct 476 { 477 input: ` 478 m { 479 nested: { 480 name: "abc", 481 } 482 } 483 `, 484 output: []interface{}{ 485 struct { 486 Nested interface{} 487 }{ 488 Nested: &EmbeddedStruct{ 489 Name: "abc", 490 }, 491 }, 492 }, 493 empty: []interface{}{ 494 &struct { 495 Nested interface{} 496 }{ 497 Nested: (*EmbeddedStruct)(nil), 498 }, 499 }, 500 }, 501 502 // Factory set properties 503 { 504 input: ` 505 m { 506 string: "abc", 507 string_ptr: "abc", 508 bool: false, 509 bool_ptr: false, 510 list: ["a", "b", "c"], 511 } 512 `, 513 output: []interface{}{ 514 struct { 515 String string 516 String_ptr *string 517 Bool bool 518 Bool_ptr *bool 519 List []string 520 }{ 521 String: "012abc", 522 String_ptr: proptools.StringPtr("abc"), 523 Bool: true, 524 Bool_ptr: proptools.BoolPtr(false), 525 List: []string{"0", "1", "2", "a", "b", "c"}, 526 }, 527 }, 528 empty: []interface{}{ 529 &struct { 530 String string 531 String_ptr *string 532 Bool bool 533 Bool_ptr *bool 534 List []string 535 }{ 536 String: "012", 537 String_ptr: proptools.StringPtr("012"), 538 Bool: true, 539 Bool_ptr: proptools.BoolPtr(true), 540 List: []string{"0", "1", "2"}, 541 }, 542 }, 543 }, 544 } 545 546 type EmbeddedStruct struct{ Name string } 547 type EmbeddedInterface interface{} 548 549 func TestUnpackProperties(t *testing.T) { 550 for _, testCase := range validUnpackTestCases { 551 r := bytes.NewBufferString(testCase.input) 552 file, errs := parser.ParseAndEval("", r, parser.NewScope(nil)) 553 if len(errs) != 0 { 554 t.Errorf("test case: %s", testCase.input) 555 t.Errorf("unexpected parse errors:") 556 for _, err := range errs { 557 t.Errorf(" %s", err) 558 } 559 t.FailNow() 560 } 561 562 for _, def := range file.Defs { 563 module, ok := def.(*parser.Module) 564 if !ok { 565 continue 566 } 567 568 var output []interface{} 569 if len(testCase.empty) > 0 { 570 output = testCase.empty 571 } else { 572 for _, p := range testCase.output { 573 output = append(output, proptools.CloneEmptyProperties(reflect.ValueOf(p)).Interface()) 574 } 575 } 576 _, errs = unpackProperties(module.Properties, output...) 577 if len(errs) != 0 && len(testCase.errs) == 0 { 578 t.Errorf("test case: %s", testCase.input) 579 t.Errorf("unexpected unpack errors:") 580 for _, err := range errs { 581 t.Errorf(" %s", err) 582 } 583 t.FailNow() 584 } else if !reflect.DeepEqual(errs, testCase.errs) { 585 t.Errorf("test case: %s", testCase.input) 586 t.Errorf("incorrect errors:") 587 t.Errorf(" expected: %+v", testCase.errs) 588 t.Errorf(" got: %+v", errs) 589 } 590 591 if len(output) != len(testCase.output) { 592 t.Fatalf("incorrect number of property structs, expected %d got %d", 593 len(testCase.output), len(output)) 594 } 595 596 for i := range output { 597 got := reflect.ValueOf(output[i]).Elem().Interface() 598 if !reflect.DeepEqual(got, testCase.output[i]) { 599 t.Errorf("test case: %s", testCase.input) 600 t.Errorf("incorrect output:") 601 t.Errorf(" expected: %+v", testCase.output[i]) 602 t.Errorf(" got: %+v", got) 603 } 604 } 605 } 606 } 607 } 608 609 func mkpos(offset, line, column int) scanner.Position { 610 return scanner.Position{ 611 Offset: offset, 612 Line: line, 613 Column: column, 614 } 615 }