blob: d6b88ab4584561d02bd3e882bab342ad24e107f8 [file] [log] [blame]
Colin Cross8e0c5112015-01-23 14:15:10 -08001// 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
Jamie Gennis1bc967e2014-05-27 16:34:41 -070015package blueprint
16
17import (
Jamie Gennis1bc967e2014-05-27 16:34:41 -070018 "bytes"
Colin Cross4adc8192015-06-22 13:38:45 -070019 "fmt"
Jamie Gennis1bc967e2014-05-27 16:34:41 -070020 "reflect"
21 "testing"
Colin Cross4adc8192015-06-22 13:38:45 -070022 "text/scanner"
23
24 "github.com/google/blueprint/parser"
25 "github.com/google/blueprint/proptools"
Jamie Gennis1bc967e2014-05-27 16:34:41 -070026)
27
28var validUnpackTestCases = []struct {
29 input string
Colin Crosse32cc802016-06-07 12:28:16 -070030 output []interface{}
Colin Crossc3d73122016-08-05 17:19:36 -070031 empty []interface{}
Colin Cross4adc8192015-06-22 13:38:45 -070032 errs []error
Jamie Gennis1bc967e2014-05-27 16:34:41 -070033}{
Colin Crossc3d73122016-08-05 17:19:36 -070034 {
35 input: `
36 m {
37 name: "abc",
38 blank: "",
39 }
Colin Cross80117682015-10-30 15:53:55 -070040 `,
Colin Crossc3d73122016-08-05 17:19:36 -070041 output: []interface{}{
Colin Crosse32cc802016-06-07 12:28:16 -070042 struct {
43 Name *string
44 Blank *string
45 Unset *string
46 }{
47 Name: proptools.StringPtr("abc"),
48 Blank: proptools.StringPtr(""),
49 Unset: nil,
50 },
Colin Cross80117682015-10-30 15:53:55 -070051 },
Colin Cross80117682015-10-30 15:53:55 -070052 },
53
Colin Crossc3d73122016-08-05 17:19:36 -070054 {
55 input: `
56 m {
57 name: "abc",
58 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -070059 `,
Colin Crossc3d73122016-08-05 17:19:36 -070060 output: []interface{}{
Colin Crosse32cc802016-06-07 12:28:16 -070061 struct {
62 Name string
63 }{
64 Name: "abc",
65 },
Jamie Gennis1bc967e2014-05-27 16:34:41 -070066 },
67 },
68
Colin Crossc3d73122016-08-05 17:19:36 -070069 {
70 input: `
71 m {
72 isGood: true,
73 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -070074 `,
Colin Crossc3d73122016-08-05 17:19:36 -070075 output: []interface{}{
Colin Crosse32cc802016-06-07 12:28:16 -070076 struct {
77 IsGood bool
78 }{
79 IsGood: true,
80 },
Jamie Gennis1bc967e2014-05-27 16:34:41 -070081 },
82 },
83
Colin Crossc3d73122016-08-05 17:19:36 -070084 {
85 input: `
86 m {
87 isGood: true,
88 isBad: false,
89 }
Colin Cross80117682015-10-30 15:53:55 -070090 `,
Colin Crossc3d73122016-08-05 17:19:36 -070091 output: []interface{}{
Colin Crosse32cc802016-06-07 12:28:16 -070092 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 },
Colin Cross80117682015-10-30 15:53:55 -0700101 },
Colin Cross80117682015-10-30 15:53:55 -0700102 },
103
Colin Crossc3d73122016-08-05 17:19:36 -0700104 {
105 input: `
106 m {
107 stuff: ["asdf", "jkl;", "qwert",
108 "uiop", "bnm,"],
109 empty: []
110 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700111 `,
Colin Crossc3d73122016-08-05 17:19:36 -0700112 output: []interface{}{
Colin Crosse32cc802016-06-07 12:28:16 -0700113 struct {
Colin Cross12392ca2018-10-02 21:57:47 -0700114 Stuff []string
115 Empty []string
116 Nil []string
117 NonString []struct{ S string } `blueprint:"mutated"`
Colin Crosse32cc802016-06-07 12:28:16 -0700118 }{
Colin Cross12392ca2018-10-02 21:57:47 -0700119 Stuff: []string{"asdf", "jkl;", "qwert", "uiop", "bnm,"},
120 Empty: []string{},
121 Nil: nil,
122 NonString: nil,
Colin Crosse32cc802016-06-07 12:28:16 -0700123 },
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700124 },
125 },
126
Colin Crossc3d73122016-08-05 17:19:36 -0700127 {
128 input: `
129 m {
130 nested: {
131 name: "abc",
132 }
Jamie Gennis87622922014-09-30 11:38:25 -0700133 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700134 `,
Colin Crossc3d73122016-08-05 17:19:36 -0700135 output: []interface{}{
Colin Crosse32cc802016-06-07 12:28:16 -0700136 struct {
137 Nested struct {
138 Name string
139 }
140 }{
141 Nested: struct{ Name string }{
142 Name: "abc",
143 },
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700144 },
145 },
146 },
147
Colin Crossc3d73122016-08-05 17:19:36 -0700148 {
149 input: `
150 m {
151 nested: {
152 name: "def",
153 }
Jamie Gennis87622922014-09-30 11:38:25 -0700154 }
Jamie Gennis87622922014-09-30 11:38:25 -0700155 `,
Colin Crossc3d73122016-08-05 17:19:36 -0700156 output: []interface{}{
Colin Crosse32cc802016-06-07 12:28:16 -0700157 struct {
158 Nested interface{}
Colin Cross4adc8192015-06-22 13:38:45 -0700159 }{
Colin Crosse32cc802016-06-07 12:28:16 -0700160 Nested: &struct{ Name string }{
161 Name: "def",
162 },
Colin Cross4adc8192015-06-22 13:38:45 -0700163 },
Colin Cross4adc8192015-06-22 13:38:45 -0700164 },
Colin Cross4adc8192015-06-22 13:38:45 -0700165 },
166
Colin Crossc3d73122016-08-05 17:19:36 -0700167 {
168 input: `
169 m {
170 nested: {
171 foo: "abc",
172 },
173 bar: false,
174 baz: ["def", "ghi"],
175 }
Colin Cross4adc8192015-06-22 13:38:45 -0700176 `,
Colin Crossc3d73122016-08-05 17:19:36 -0700177 output: []interface{}{
Colin Crosse32cc802016-06-07 12:28:16 -0700178 struct {
179 Nested struct {
180 Foo string
181 }
182 Bar bool
183 Baz []string
184 }{
185 Nested: struct{ Foo string }{
186 Foo: "abc",
187 },
188 Bar: false,
189 Baz: []string{"def", "ghi"},
Colin Cross4adc8192015-06-22 13:38:45 -0700190 },
Colin Crosse32cc802016-06-07 12:28:16 -0700191 },
Colin Crosse32cc802016-06-07 12:28:16 -0700192 },
193
Colin Crossc3d73122016-08-05 17:19:36 -0700194 {
195 input: `
196 m {
197 nested: {
198 foo: "abc",
199 },
200 bar: false,
201 baz: ["def", "ghi"],
202 }
Colin Crosse32cc802016-06-07 12:28:16 -0700203 `,
Colin Crossc3d73122016-08-05 17:19:36 -0700204 output: []interface{}{
Colin Crosse32cc802016-06-07 12:28:16 -0700205 struct {
206 Nested struct {
207 Foo string `allowNested:"true"`
208 } `blueprint:"filter(allowNested:\"true\")"`
209 Bar bool
210 Baz []string
211 }{
212 Nested: struct {
213 Foo string `allowNested:"true"`
214 }{
215 Foo: "abc",
216 },
217 Bar: false,
218 Baz: []string{"def", "ghi"},
219 },
220 },
Colin Crosse32cc802016-06-07 12:28:16 -0700221 },
222
Colin Crossc3d73122016-08-05 17:19:36 -0700223 {
224 input: `
225 m {
226 nested: {
227 foo: "abc",
228 },
229 bar: false,
230 baz: ["def", "ghi"],
231 }
Colin Crosse32cc802016-06-07 12:28:16 -0700232 `,
Colin Crossc3d73122016-08-05 17:19:36 -0700233 output: []interface{}{
Colin Crosse32cc802016-06-07 12:28:16 -0700234 struct {
235 Nested struct {
236 Foo string
237 } `blueprint:"filter(allowNested:\"true\")"`
238 Bar bool
239 Baz []string
240 }{
241 Nested: struct{ Foo string }{
242 Foo: "",
243 },
244 Bar: false,
245 Baz: []string{"def", "ghi"},
246 },
Colin Cross4adc8192015-06-22 13:38:45 -0700247 },
Colin Crossc3d73122016-08-05 17:19:36 -0700248 errs: []error{
Colin Cross2c628442016-10-07 17:13:10 -0700249 &BlueprintError{
Colin Cross4adc8192015-06-22 13:38:45 -0700250 Err: fmt.Errorf("filtered field nested.foo cannot be set in a Blueprint file"),
Colin Crossc3d73122016-08-05 17:19:36 -0700251 Pos: mkpos(30, 4, 9),
Colin Cross4adc8192015-06-22 13:38:45 -0700252 },
253 },
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700254 },
Colin Cross9d1469d2015-11-20 17:03:25 -0800255
256 // Anonymous struct
Colin Crossc3d73122016-08-05 17:19:36 -0700257 {
258 input: `
259 m {
260 name: "abc",
261 nested: {
262 name: "def",
263 },
264 }
Colin Cross9d1469d2015-11-20 17:03:25 -0800265 `,
Colin Crossc3d73122016-08-05 17:19:36 -0700266 output: []interface{}{
Colin Crosse32cc802016-06-07 12:28:16 -0700267 struct {
Colin Cross9d1469d2015-11-20 17:03:25 -0800268 EmbeddedStruct
Colin Crosse32cc802016-06-07 12:28:16 -0700269 Nested struct {
270 EmbeddedStruct
271 }
Colin Cross9d1469d2015-11-20 17:03:25 -0800272 }{
273 EmbeddedStruct: EmbeddedStruct{
Colin Crosse32cc802016-06-07 12:28:16 -0700274 Name: "abc",
275 },
276 Nested: struct {
277 EmbeddedStruct
278 }{
279 EmbeddedStruct: EmbeddedStruct{
280 Name: "def",
281 },
Colin Cross9d1469d2015-11-20 17:03:25 -0800282 },
283 },
284 },
Colin Cross9d1469d2015-11-20 17:03:25 -0800285 },
286
287 // Anonymous interface
Colin Crossc3d73122016-08-05 17:19:36 -0700288 {
289 input: `
290 m {
291 name: "abc",
292 nested: {
293 name: "def",
294 },
295 }
Colin Cross9d1469d2015-11-20 17:03:25 -0800296 `,
Colin Crossc3d73122016-08-05 17:19:36 -0700297 output: []interface{}{
Colin Crosse32cc802016-06-07 12:28:16 -0700298 struct {
Colin Cross9d1469d2015-11-20 17:03:25 -0800299 EmbeddedInterface
Colin Crosse32cc802016-06-07 12:28:16 -0700300 Nested struct {
301 EmbeddedInterface
302 }
Colin Cross9d1469d2015-11-20 17:03:25 -0800303 }{
304 EmbeddedInterface: &struct{ Name string }{
Colin Crosse32cc802016-06-07 12:28:16 -0700305 Name: "abc",
306 },
307 Nested: struct {
308 EmbeddedInterface
309 }{
310 EmbeddedInterface: &struct{ Name string }{
311 Name: "def",
312 },
Colin Cross9d1469d2015-11-20 17:03:25 -0800313 },
314 },
315 },
Colin Cross9d1469d2015-11-20 17:03:25 -0800316 },
317
318 // Anonymous struct with name collision
Colin Crossc3d73122016-08-05 17:19:36 -0700319 {
320 input: `
321 m {
322 name: "abc",
323 nested: {
324 name: "def",
325 },
326 }
Colin Cross9d1469d2015-11-20 17:03:25 -0800327 `,
Colin Crossc3d73122016-08-05 17:19:36 -0700328 output: []interface{}{
Colin Crosse32cc802016-06-07 12:28:16 -0700329 struct {
Colin Cross9d1469d2015-11-20 17:03:25 -0800330 Name string
331 EmbeddedStruct
Colin Crosse32cc802016-06-07 12:28:16 -0700332 Nested struct {
333 Name string
334 EmbeddedStruct
335 }
Colin Cross9d1469d2015-11-20 17:03:25 -0800336 }{
Colin Crosse32cc802016-06-07 12:28:16 -0700337 Name: "abc",
Colin Cross9d1469d2015-11-20 17:03:25 -0800338 EmbeddedStruct: EmbeddedStruct{
Colin Crosse32cc802016-06-07 12:28:16 -0700339 Name: "abc",
340 },
341 Nested: struct {
342 Name string
343 EmbeddedStruct
344 }{
Colin Cross9d1469d2015-11-20 17:03:25 -0800345 Name: "def",
Colin Crosse32cc802016-06-07 12:28:16 -0700346 EmbeddedStruct: EmbeddedStruct{
347 Name: "def",
348 },
Colin Cross9d1469d2015-11-20 17:03:25 -0800349 },
350 },
351 },
Colin Cross9d1469d2015-11-20 17:03:25 -0800352 },
353
354 // Anonymous interface with name collision
Colin Crossc3d73122016-08-05 17:19:36 -0700355 {
356 input: `
357 m {
358 name: "abc",
359 nested: {
360 name: "def",
361 },
362 }
Colin Cross9d1469d2015-11-20 17:03:25 -0800363 `,
Colin Crossc3d73122016-08-05 17:19:36 -0700364 output: []interface{}{
Colin Crosse32cc802016-06-07 12:28:16 -0700365 struct {
Colin Cross9d1469d2015-11-20 17:03:25 -0800366 Name string
367 EmbeddedInterface
Colin Crosse32cc802016-06-07 12:28:16 -0700368 Nested struct {
369 Name string
370 EmbeddedInterface
371 }
Colin Cross9d1469d2015-11-20 17:03:25 -0800372 }{
Colin Crosse32cc802016-06-07 12:28:16 -0700373 Name: "abc",
Colin Cross9d1469d2015-11-20 17:03:25 -0800374 EmbeddedInterface: &struct{ Name string }{
Colin Crosse32cc802016-06-07 12:28:16 -0700375 Name: "abc",
376 },
377 Nested: struct {
378 Name string
379 EmbeddedInterface
380 }{
Colin Cross9d1469d2015-11-20 17:03:25 -0800381 Name: "def",
Colin Crosse32cc802016-06-07 12:28:16 -0700382 EmbeddedInterface: &struct{ Name string }{
383 Name: "def",
384 },
Colin Cross9d1469d2015-11-20 17:03:25 -0800385 },
386 },
387 },
Colin Cross9d1469d2015-11-20 17:03:25 -0800388 },
Colin Crosse32cc802016-06-07 12:28:16 -0700389
390 // Variables
Colin Crossc3d73122016-08-05 17:19:36 -0700391 {
392 input: `
393 list = ["abc"]
394 string = "def"
395 list_with_variable = [string]
396 m {
397 name: string,
398 list: list,
399 list2: list_with_variable,
400 }
Colin Crosse32cc802016-06-07 12:28:16 -0700401 `,
Colin Crossc3d73122016-08-05 17:19:36 -0700402 output: []interface{}{
Colin Crosse32cc802016-06-07 12:28:16 -0700403 struct {
404 Name string
405 List []string
406 List2 []string
407 }{
408 Name: "def",
409 List: []string{"abc"},
410 List2: []string{"def"},
411 },
412 },
Colin Crosse32cc802016-06-07 12:28:16 -0700413 },
414
415 // Multiple property structs
Colin Crossc3d73122016-08-05 17:19:36 -0700416 {
417 input: `
418 m {
419 nested: {
420 name: "abc",
421 }
Colin Crosse32cc802016-06-07 12:28:16 -0700422 }
Colin Crosse32cc802016-06-07 12:28:16 -0700423 `,
Colin Crossc3d73122016-08-05 17:19:36 -0700424 output: []interface{}{
Colin Crosse32cc802016-06-07 12:28:16 -0700425 struct {
426 Nested struct {
427 Name string
428 }
429 }{
430 Nested: struct{ Name string }{
431 Name: "abc",
432 },
433 },
434 struct {
435 Nested struct {
436 Name string
437 }
438 }{
439 Nested: struct{ Name string }{
440 Name: "abc",
441 },
442 },
443 struct {
444 }{},
445 },
Colin Crossc3d73122016-08-05 17:19:36 -0700446 },
447
448 // Nil pointer to struct
449 {
450 input: `
451 m {
452 nested: {
453 name: "abc",
454 }
455 }
456 `,
457 output: []interface{}{
458 struct {
459 Nested *struct {
460 Name string
461 }
462 }{
463 Nested: &struct{ Name string }{
464 Name: "abc",
465 },
466 },
467 },
468 empty: []interface{}{
469 &struct {
470 Nested *struct {
471 Name string
472 }
473 }{},
474 },
475 },
476
477 // Interface containing nil pointer to struct
478 {
479 input: `
480 m {
481 nested: {
482 name: "abc",
483 }
484 }
485 `,
486 output: []interface{}{
487 struct {
488 Nested interface{}
489 }{
490 Nested: &EmbeddedStruct{
491 Name: "abc",
492 },
493 },
494 },
495 empty: []interface{}{
496 &struct {
497 Nested interface{}
498 }{
499 Nested: (*EmbeddedStruct)(nil),
500 },
501 },
Colin Crosse32cc802016-06-07 12:28:16 -0700502 },
Colin Cross05b36072017-07-28 17:51:37 -0700503
504 // Factory set properties
505 {
506 input: `
507 m {
508 string: "abc",
509 string_ptr: "abc",
510 bool: false,
511 bool_ptr: false,
512 list: ["a", "b", "c"],
513 }
514 `,
515 output: []interface{}{
516 struct {
517 String string
518 String_ptr *string
519 Bool bool
520 Bool_ptr *bool
521 List []string
522 }{
523 String: "012abc",
524 String_ptr: proptools.StringPtr("abc"),
525 Bool: true,
526 Bool_ptr: proptools.BoolPtr(false),
527 List: []string{"0", "1", "2", "a", "b", "c"},
528 },
529 },
530 empty: []interface{}{
531 &struct {
532 String string
533 String_ptr *string
534 Bool bool
535 Bool_ptr *bool
536 List []string
537 }{
538 String: "012",
539 String_ptr: proptools.StringPtr("012"),
540 Bool: true,
541 Bool_ptr: proptools.BoolPtr(true),
542 List: []string{"0", "1", "2"},
543 },
544 },
545 },
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700546}
547
Colin Cross9d1469d2015-11-20 17:03:25 -0800548type EmbeddedStruct struct{ Name string }
549type EmbeddedInterface interface{}
550
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700551func TestUnpackProperties(t *testing.T) {
552 for _, testCase := range validUnpackTestCases {
553 r := bytes.NewBufferString(testCase.input)
Colin Crosse32cc802016-06-07 12:28:16 -0700554 file, errs := parser.ParseAndEval("", r, parser.NewScope(nil))
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700555 if len(errs) != 0 {
556 t.Errorf("test case: %s", testCase.input)
557 t.Errorf("unexpected parse errors:")
558 for _, err := range errs {
559 t.Errorf(" %s", err)
560 }
561 t.FailNow()
562 }
563
Colin Crosse32cc802016-06-07 12:28:16 -0700564 for _, def := range file.Defs {
565 module, ok := def.(*parser.Module)
566 if !ok {
567 continue
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700568 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700569
Colin Crossc3d73122016-08-05 17:19:36 -0700570 var output []interface{}
571 if len(testCase.empty) > 0 {
572 output = testCase.empty
573 } else {
574 for _, p := range testCase.output {
575 output = append(output, proptools.CloneEmptyProperties(reflect.ValueOf(p)).Interface())
576 }
Colin Crosse32cc802016-06-07 12:28:16 -0700577 }
578 _, errs = unpackProperties(module.Properties, output...)
579 if len(errs) != 0 && len(testCase.errs) == 0 {
580 t.Errorf("test case: %s", testCase.input)
581 t.Errorf("unexpected unpack errors:")
582 for _, err := range errs {
583 t.Errorf(" %s", err)
584 }
585 t.FailNow()
586 } else if !reflect.DeepEqual(errs, testCase.errs) {
587 t.Errorf("test case: %s", testCase.input)
588 t.Errorf("incorrect errors:")
589 t.Errorf(" expected: %+v", testCase.errs)
590 t.Errorf(" got: %+v", errs)
591 }
592
593 if len(output) != len(testCase.output) {
594 t.Fatalf("incorrect number of property structs, expected %d got %d",
595 len(testCase.output), len(output))
596 }
597
598 for i := range output {
599 got := reflect.ValueOf(output[i]).Elem().Interface()
600 if !reflect.DeepEqual(got, testCase.output[i]) {
601 t.Errorf("test case: %s", testCase.input)
602 t.Errorf("incorrect output:")
603 t.Errorf(" expected: %+v", testCase.output[i])
604 t.Errorf(" got: %+v", got)
605 }
606 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700607 }
608 }
609}
Colin Cross017ed2e2016-05-31 15:53:32 -0700610
611func mkpos(offset, line, column int) scanner.Position {
612 return scanner.Position{
613 Offset: offset,
614 Line: line,
615 Column: column,
616 }
617}