• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
15package blueprint
16
17import (
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
28var 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				NonString []struct{ S string } `blueprint:"mutated"`
118			}{
119				Stuff:     []string{"asdf", "jkl;", "qwert", "uiop", "bnm,"},
120				Empty:     []string{},
121				Nil:       nil,
122				NonString: nil,
123			},
124		},
125	},
126
127	{
128		input: `
129			m {
130				nested: {
131					name: "abc",
132				}
133			}
134		`,
135		output: []interface{}{
136			struct {
137				Nested struct {
138					Name string
139				}
140			}{
141				Nested: struct{ Name string }{
142					Name: "abc",
143				},
144			},
145		},
146	},
147
148	{
149		input: `
150			m {
151				nested: {
152					name: "def",
153				}
154			}
155		`,
156		output: []interface{}{
157			struct {
158				Nested interface{}
159			}{
160				Nested: &struct{ Name string }{
161					Name: "def",
162				},
163			},
164		},
165	},
166
167	{
168		input: `
169			m {
170				nested: {
171					foo: "abc",
172				},
173				bar: false,
174				baz: ["def", "ghi"],
175			}
176		`,
177		output: []interface{}{
178			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"},
190			},
191		},
192	},
193
194	{
195		input: `
196			m {
197				nested: {
198					foo: "abc",
199				},
200				bar: false,
201				baz: ["def", "ghi"],
202			}
203		`,
204		output: []interface{}{
205			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		},
221	},
222
223	{
224		input: `
225			m {
226				nested: {
227					foo: "abc",
228				},
229				bar: false,
230				baz: ["def", "ghi"],
231			}
232		`,
233		output: []interface{}{
234			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			},
247		},
248		errs: []error{
249			&BlueprintError{
250				Err: fmt.Errorf("filtered field nested.foo cannot be set in a Blueprint file"),
251				Pos: mkpos(30, 4, 9),
252			},
253		},
254	},
255
256	// Anonymous struct
257	{
258		input: `
259			m {
260				name: "abc",
261				nested: {
262					name: "def",
263				},
264			}
265		`,
266		output: []interface{}{
267			struct {
268				EmbeddedStruct
269				Nested struct {
270					EmbeddedStruct
271				}
272			}{
273				EmbeddedStruct: EmbeddedStruct{
274					Name: "abc",
275				},
276				Nested: struct {
277					EmbeddedStruct
278				}{
279					EmbeddedStruct: EmbeddedStruct{
280						Name: "def",
281					},
282				},
283			},
284		},
285	},
286
287	// Anonymous interface
288	{
289		input: `
290			m {
291				name: "abc",
292				nested: {
293					name: "def",
294				},
295			}
296		`,
297		output: []interface{}{
298			struct {
299				EmbeddedInterface
300				Nested struct {
301					EmbeddedInterface
302				}
303			}{
304				EmbeddedInterface: &struct{ Name string }{
305					Name: "abc",
306				},
307				Nested: struct {
308					EmbeddedInterface
309				}{
310					EmbeddedInterface: &struct{ Name string }{
311						Name: "def",
312					},
313				},
314			},
315		},
316	},
317
318	// Anonymous struct with name collision
319	{
320		input: `
321			m {
322				name: "abc",
323				nested: {
324					name: "def",
325				},
326			}
327		`,
328		output: []interface{}{
329			struct {
330				Name string
331				EmbeddedStruct
332				Nested struct {
333					Name string
334					EmbeddedStruct
335				}
336			}{
337				Name: "abc",
338				EmbeddedStruct: EmbeddedStruct{
339					Name: "abc",
340				},
341				Nested: struct {
342					Name string
343					EmbeddedStruct
344				}{
345					Name: "def",
346					EmbeddedStruct: EmbeddedStruct{
347						Name: "def",
348					},
349				},
350			},
351		},
352	},
353
354	// Anonymous interface with name collision
355	{
356		input: `
357			m {
358				name: "abc",
359				nested: {
360					name: "def",
361				},
362			}
363		`,
364		output: []interface{}{
365			struct {
366				Name string
367				EmbeddedInterface
368				Nested struct {
369					Name string
370					EmbeddedInterface
371				}
372			}{
373				Name: "abc",
374				EmbeddedInterface: &struct{ Name string }{
375					Name: "abc",
376				},
377				Nested: struct {
378					Name string
379					EmbeddedInterface
380				}{
381					Name: "def",
382					EmbeddedInterface: &struct{ Name string }{
383						Name: "def",
384					},
385				},
386			},
387		},
388	},
389
390	// Variables
391	{
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			}
401		`,
402		output: []interface{}{
403			struct {
404				Name  string
405				List  []string
406				List2 []string
407			}{
408				Name:  "def",
409				List:  []string{"abc"},
410				List2: []string{"def"},
411			},
412		},
413	},
414
415	// Multiple property structs
416	{
417		input: `
418			m {
419				nested: {
420					name: "abc",
421				}
422			}
423		`,
424		output: []interface{}{
425			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		},
446	},
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		},
502	},
503
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	},
546}
547
548type EmbeddedStruct struct{ Name string }
549type EmbeddedInterface interface{}
550
551func TestUnpackProperties(t *testing.T) {
552	for _, testCase := range validUnpackTestCases {
553		r := bytes.NewBufferString(testCase.input)
554		file, errs := parser.ParseAndEval("", r, parser.NewScope(nil))
555		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
564		for _, def := range file.Defs {
565			module, ok := def.(*parser.Module)
566			if !ok {
567				continue
568			}
569
570			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				}
577			}
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			}
607		}
608	}
609}
610
611func mkpos(offset, line, column int) scanner.Position {
612	return scanner.Position{
613		Offset: offset,
614		Line:   line,
615		Column: column,
616	}
617}
618