Home | History | Annotate | Download | only in blueprint
      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