blob: 7e2751d3847785745ae6c86f0c16b52e25424c9b [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
Colin Crossf27c5e42020-01-02 09:37:49 -080015package proptools
Jamie Gennis1bc967e2014-05-27 16:34:41 -070016
17import (
Jamie Gennis1bc967e2014-05-27 16:34:41 -070018 "bytes"
19 "reflect"
Sasha Smundak29fdcad2020-02-11 22:39:47 -080020
Jamie Gennis1bc967e2014-05-27 16:34:41 -070021 "testing"
Colin Cross4adc8192015-06-22 13:38:45 -070022
23 "github.com/google/blueprint/parser"
Jamie Gennis1bc967e2014-05-27 16:34:41 -070024)
25
26var validUnpackTestCases = []struct {
Colin Cross461d9502020-01-23 22:33:21 -080027 name string
Jamie Gennis1bc967e2014-05-27 16:34:41 -070028 input string
Colin Crosse32cc802016-06-07 12:28:16 -070029 output []interface{}
Colin Crossc3d73122016-08-05 17:19:36 -070030 empty []interface{}
Colin Cross4adc8192015-06-22 13:38:45 -070031 errs []error
Jamie Gennis1bc967e2014-05-27 16:34:41 -070032}{
Colin Crossc3d73122016-08-05 17:19:36 -070033 {
Colin Cross461d9502020-01-23 22:33:21 -080034 name: "blank and unset",
Colin Crossc3d73122016-08-05 17:19:36 -070035 input: `
36 m {
Colin Crossf27c5e42020-01-02 09:37:49 -080037 s: "abc",
Colin Crossc3d73122016-08-05 17:19:36 -070038 blank: "",
39 }
Colin Cross80117682015-10-30 15:53:55 -070040 `,
Colin Crossc3d73122016-08-05 17:19:36 -070041 output: []interface{}{
Colin Cross5d57b2d2020-01-27 16:14:31 -080042 &struct {
Colin Crossf27c5e42020-01-02 09:37:49 -080043 S *string
Colin Crosse32cc802016-06-07 12:28:16 -070044 Blank *string
45 Unset *string
46 }{
Colin Crossf27c5e42020-01-02 09:37:49 -080047 S: StringPtr("abc"),
48 Blank: StringPtr(""),
Colin Crosse32cc802016-06-07 12:28:16 -070049 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 {
Colin Cross461d9502020-01-23 22:33:21 -080055 name: "string",
Colin Crossc3d73122016-08-05 17:19:36 -070056 input: `
57 m {
Colin Crossf27c5e42020-01-02 09:37:49 -080058 s: "abc",
Colin Crossc3d73122016-08-05 17:19:36 -070059 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -070060 `,
Colin Crossc3d73122016-08-05 17:19:36 -070061 output: []interface{}{
Colin Cross5d57b2d2020-01-27 16:14:31 -080062 &struct {
Colin Crossf27c5e42020-01-02 09:37:49 -080063 S string
Colin Crosse32cc802016-06-07 12:28:16 -070064 }{
Colin Crossf27c5e42020-01-02 09:37:49 -080065 S: "abc",
Colin Crosse32cc802016-06-07 12:28:16 -070066 },
Jamie Gennis1bc967e2014-05-27 16:34:41 -070067 },
68 },
69
Colin Crossc3d73122016-08-05 17:19:36 -070070 {
Colin Cross461d9502020-01-23 22:33:21 -080071 name: "bool",
Colin Crossc3d73122016-08-05 17:19:36 -070072 input: `
73 m {
74 isGood: true,
75 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -070076 `,
Colin Crossc3d73122016-08-05 17:19:36 -070077 output: []interface{}{
Colin Cross5d57b2d2020-01-27 16:14:31 -080078 &struct {
Colin Crosse32cc802016-06-07 12:28:16 -070079 IsGood bool
80 }{
81 IsGood: true,
82 },
Jamie Gennis1bc967e2014-05-27 16:34:41 -070083 },
84 },
85
Colin Crossc3d73122016-08-05 17:19:36 -070086 {
Colin Cross461d9502020-01-23 22:33:21 -080087 name: "boolptr",
Colin Crossc3d73122016-08-05 17:19:36 -070088 input: `
89 m {
90 isGood: true,
91 isBad: false,
92 }
Colin Cross80117682015-10-30 15:53:55 -070093 `,
Colin Crossc3d73122016-08-05 17:19:36 -070094 output: []interface{}{
Colin Cross5d57b2d2020-01-27 16:14:31 -080095 &struct {
Colin Crosse32cc802016-06-07 12:28:16 -070096 IsGood *bool
97 IsBad *bool
98 IsUgly *bool
99 }{
Colin Crossf27c5e42020-01-02 09:37:49 -0800100 IsGood: BoolPtr(true),
101 IsBad: BoolPtr(false),
Colin Crosse32cc802016-06-07 12:28:16 -0700102 IsUgly: nil,
103 },
Colin Cross80117682015-10-30 15:53:55 -0700104 },
Colin Cross80117682015-10-30 15:53:55 -0700105 },
106
Colin Crossc3d73122016-08-05 17:19:36 -0700107 {
Colin Cross461d9502020-01-23 22:33:21 -0800108 name: "slice",
Colin Crossc3d73122016-08-05 17:19:36 -0700109 input: `
110 m {
111 stuff: ["asdf", "jkl;", "qwert",
112 "uiop", "bnm,"],
113 empty: []
114 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700115 `,
Colin Crossc3d73122016-08-05 17:19:36 -0700116 output: []interface{}{
Colin Cross5d57b2d2020-01-27 16:14:31 -0800117 &struct {
Colin Cross12392ca2018-10-02 21:57:47 -0700118 Stuff []string
119 Empty []string
120 Nil []string
121 NonString []struct{ S string } `blueprint:"mutated"`
Colin Crosse32cc802016-06-07 12:28:16 -0700122 }{
Colin Cross12392ca2018-10-02 21:57:47 -0700123 Stuff: []string{"asdf", "jkl;", "qwert", "uiop", "bnm,"},
124 Empty: []string{},
125 Nil: nil,
126 NonString: nil,
Colin Crosse32cc802016-06-07 12:28:16 -0700127 },
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700128 },
129 },
130
Colin Crossc3d73122016-08-05 17:19:36 -0700131 {
Colin Cross461d9502020-01-23 22:33:21 -0800132 name: "double nested",
133 input: `
134 m {
135 nested: {
136 nested: {
137 s: "abc",
138 },
139 },
140 }
141 `,
142 output: []interface{}{
143 &struct {
144 Nested struct {
145 Nested struct {
146 S string
147 }
148 }
149 }{
150 Nested: struct{ Nested struct{ S string } }{
151 Nested: struct{ S string }{
152 S: "abc",
153 },
154 },
155 },
156 },
157 },
158
159 {
160 name: "nested",
Colin Crossc3d73122016-08-05 17:19:36 -0700161 input: `
162 m {
163 nested: {
Colin Crossf27c5e42020-01-02 09:37:49 -0800164 s: "abc",
Colin Crossc3d73122016-08-05 17:19:36 -0700165 }
Jamie Gennis87622922014-09-30 11:38:25 -0700166 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700167 `,
Colin Crossc3d73122016-08-05 17:19:36 -0700168 output: []interface{}{
Colin Cross5d57b2d2020-01-27 16:14:31 -0800169 &struct {
Colin Crosse32cc802016-06-07 12:28:16 -0700170 Nested struct {
Colin Crossf27c5e42020-01-02 09:37:49 -0800171 S string
Colin Crosse32cc802016-06-07 12:28:16 -0700172 }
173 }{
Colin Crossf27c5e42020-01-02 09:37:49 -0800174 Nested: struct{ S string }{
175 S: "abc",
Colin Crosse32cc802016-06-07 12:28:16 -0700176 },
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700177 },
178 },
179 },
180
Colin Crossc3d73122016-08-05 17:19:36 -0700181 {
Colin Cross461d9502020-01-23 22:33:21 -0800182 name: "nested interface",
Colin Crossc3d73122016-08-05 17:19:36 -0700183 input: `
184 m {
185 nested: {
Colin Crossf27c5e42020-01-02 09:37:49 -0800186 s: "def",
Colin Crossc3d73122016-08-05 17:19:36 -0700187 }
Jamie Gennis87622922014-09-30 11:38:25 -0700188 }
Jamie Gennis87622922014-09-30 11:38:25 -0700189 `,
Colin Crossc3d73122016-08-05 17:19:36 -0700190 output: []interface{}{
Colin Cross5d57b2d2020-01-27 16:14:31 -0800191 &struct {
Colin Crosse32cc802016-06-07 12:28:16 -0700192 Nested interface{}
Colin Cross4adc8192015-06-22 13:38:45 -0700193 }{
Colin Crossf27c5e42020-01-02 09:37:49 -0800194 Nested: &struct{ S string }{
195 S: "def",
Colin Crosse32cc802016-06-07 12:28:16 -0700196 },
Colin Cross4adc8192015-06-22 13:38:45 -0700197 },
Colin Cross4adc8192015-06-22 13:38:45 -0700198 },
Colin Cross4adc8192015-06-22 13:38:45 -0700199 },
200
Colin Crossc3d73122016-08-05 17:19:36 -0700201 {
Colin Cross461d9502020-01-23 22:33:21 -0800202 name: "mixed",
Colin Crossc3d73122016-08-05 17:19:36 -0700203 input: `
204 m {
205 nested: {
206 foo: "abc",
207 },
208 bar: false,
209 baz: ["def", "ghi"],
210 }
Colin Cross4adc8192015-06-22 13:38:45 -0700211 `,
Colin Crossc3d73122016-08-05 17:19:36 -0700212 output: []interface{}{
Colin Cross5d57b2d2020-01-27 16:14:31 -0800213 &struct {
Colin Crosse32cc802016-06-07 12:28:16 -0700214 Nested struct {
215 Foo string
216 }
217 Bar bool
218 Baz []string
219 }{
220 Nested: struct{ Foo string }{
221 Foo: "abc",
222 },
223 Bar: false,
224 Baz: []string{"def", "ghi"},
Colin Cross4adc8192015-06-22 13:38:45 -0700225 },
Colin Crosse32cc802016-06-07 12:28:16 -0700226 },
Colin Crosse32cc802016-06-07 12:28:16 -0700227 },
228
Colin Crossc3d73122016-08-05 17:19:36 -0700229 {
Colin Cross461d9502020-01-23 22:33:21 -0800230 name: "filter",
Colin Crossc3d73122016-08-05 17:19:36 -0700231 input: `
232 m {
233 nested: {
234 foo: "abc",
235 },
236 bar: false,
237 baz: ["def", "ghi"],
238 }
Colin Crosse32cc802016-06-07 12:28:16 -0700239 `,
Colin Crossc3d73122016-08-05 17:19:36 -0700240 output: []interface{}{
Colin Cross5d57b2d2020-01-27 16:14:31 -0800241 &struct {
Colin Crosse32cc802016-06-07 12:28:16 -0700242 Nested struct {
243 Foo string `allowNested:"true"`
244 } `blueprint:"filter(allowNested:\"true\")"`
245 Bar bool
246 Baz []string
247 }{
248 Nested: struct {
249 Foo string `allowNested:"true"`
250 }{
251 Foo: "abc",
252 },
253 Bar: false,
254 Baz: []string{"def", "ghi"},
255 },
256 },
Colin Crosse32cc802016-06-07 12:28:16 -0700257 },
258
Sasha Smundak29fdcad2020-02-11 22:39:47 -0800259 // List of maps
260 {
Colin Cross461d9502020-01-23 22:33:21 -0800261 name: "list of structs",
Sasha Smundak29fdcad2020-02-11 22:39:47 -0800262 input: `
263 m {
264 mapslist: [
265 {
266 foo: "abc",
267 bar: true,
268 },
269 {
270 foo: "def",
271 bar: false,
272 }
273 ],
274 }
275 `,
276 output: []interface{}{
277 &struct {
278 Mapslist []struct {
279 Foo string
280 Bar bool
281 }
282 }{
283 Mapslist: []struct {
284 Foo string
285 Bar bool
286 }{
287 {Foo: "abc", Bar: true},
288 {Foo: "def", Bar: false},
289 },
290 },
291 },
292 },
293
294 // List of pointers to structs
295 {
Colin Cross461d9502020-01-23 22:33:21 -0800296 name: "list of pointers to structs",
Sasha Smundak29fdcad2020-02-11 22:39:47 -0800297 input: `
298 m {
299 mapslist: [
300 {
301 foo: "abc",
302 bar: true,
303 },
304 {
305 foo: "def",
306 bar: false,
307 }
308 ],
309 }
310 `,
311 output: []interface{}{
312 &struct {
313 Mapslist []*struct {
314 Foo string
315 Bar bool
316 }
317 }{
318 Mapslist: []*struct {
319 Foo string
320 Bar bool
321 }{
322 {Foo: "abc", Bar: true},
323 {Foo: "def", Bar: false},
324 },
325 },
326 },
327 },
328
329 // List of lists
330 {
Colin Cross461d9502020-01-23 22:33:21 -0800331 name: "list of lists",
Sasha Smundak29fdcad2020-02-11 22:39:47 -0800332 input: `
333 m {
334 listoflists: [
335 ["abc",],
336 ["def",],
337 ],
338 }
339 `,
340 output: []interface{}{
341 &struct {
342 Listoflists [][]string
343 }{
344 Listoflists: [][]string{
345 []string{"abc"},
346 []string{"def"},
347 },
348 },
349 },
350 },
351
352 // Multilevel
353 {
Colin Cross461d9502020-01-23 22:33:21 -0800354 name: "multilevel",
Sasha Smundak29fdcad2020-02-11 22:39:47 -0800355 input: `
356 m {
357 name: "mymodule",
358 flag: true,
359 settings: ["foo1", "foo2", "foo3",],
360 perarch: {
361 arm: "32",
362 arm64: "64",
363 },
364 configvars: [
365 { var: "var1", values: ["1.1", "1.2", ], },
366 { var: "var2", values: ["2.1", ], },
367 ],
368 }
369 `,
370 output: []interface{}{
371 &struct {
372 Name string
373 Flag bool
374 Settings []string
375 Perarch *struct {
376 Arm string
377 Arm64 string
378 }
379 Configvars []struct {
380 Var string
381 Values []string
382 }
383 }{
384 Name: "mymodule",
385 Flag: true,
386 Settings: []string{"foo1", "foo2", "foo3"},
387 Perarch: &struct {
388 Arm string
389 Arm64 string
390 }{Arm: "32", Arm64: "64"},
391 Configvars: []struct {
392 Var string
393 Values []string
394 }{
395 {Var: "var1", Values: []string{"1.1", "1.2"}},
396 {Var: "var2", Values: []string{"2.1"}},
397 },
398 },
399 },
400 },
Colin Cross9d1469d2015-11-20 17:03:25 -0800401 // Anonymous struct
Colin Crossc3d73122016-08-05 17:19:36 -0700402 {
Colin Cross461d9502020-01-23 22:33:21 -0800403 name: "embedded struct",
Colin Crossc3d73122016-08-05 17:19:36 -0700404 input: `
405 m {
Colin Crossf27c5e42020-01-02 09:37:49 -0800406 s: "abc",
Colin Crossc3d73122016-08-05 17:19:36 -0700407 nested: {
Colin Crossf27c5e42020-01-02 09:37:49 -0800408 s: "def",
Colin Crossc3d73122016-08-05 17:19:36 -0700409 },
410 }
Colin Cross9d1469d2015-11-20 17:03:25 -0800411 `,
Colin Crossc3d73122016-08-05 17:19:36 -0700412 output: []interface{}{
Colin Cross5d57b2d2020-01-27 16:14:31 -0800413 &struct {
Colin Cross9d1469d2015-11-20 17:03:25 -0800414 EmbeddedStruct
Colin Crosse32cc802016-06-07 12:28:16 -0700415 Nested struct {
416 EmbeddedStruct
417 }
Colin Cross9d1469d2015-11-20 17:03:25 -0800418 }{
419 EmbeddedStruct: EmbeddedStruct{
Colin Crossf27c5e42020-01-02 09:37:49 -0800420 S: "abc",
Colin Crosse32cc802016-06-07 12:28:16 -0700421 },
422 Nested: struct {
423 EmbeddedStruct
424 }{
425 EmbeddedStruct: EmbeddedStruct{
Colin Crossf27c5e42020-01-02 09:37:49 -0800426 S: "def",
Colin Crosse32cc802016-06-07 12:28:16 -0700427 },
Colin Cross9d1469d2015-11-20 17:03:25 -0800428 },
429 },
430 },
Colin Cross9d1469d2015-11-20 17:03:25 -0800431 },
432
433 // Anonymous interface
Colin Crossc3d73122016-08-05 17:19:36 -0700434 {
Colin Cross461d9502020-01-23 22:33:21 -0800435 name: "embedded interface",
Colin Crossc3d73122016-08-05 17:19:36 -0700436 input: `
437 m {
Colin Crossf27c5e42020-01-02 09:37:49 -0800438 s: "abc",
Colin Crossc3d73122016-08-05 17:19:36 -0700439 nested: {
Colin Crossf27c5e42020-01-02 09:37:49 -0800440 s: "def",
Colin Crossc3d73122016-08-05 17:19:36 -0700441 },
442 }
Colin Cross9d1469d2015-11-20 17:03:25 -0800443 `,
Colin Crossc3d73122016-08-05 17:19:36 -0700444 output: []interface{}{
Colin Cross5d57b2d2020-01-27 16:14:31 -0800445 &struct {
Colin Cross9d1469d2015-11-20 17:03:25 -0800446 EmbeddedInterface
Colin Crosse32cc802016-06-07 12:28:16 -0700447 Nested struct {
448 EmbeddedInterface
449 }
Colin Cross9d1469d2015-11-20 17:03:25 -0800450 }{
Colin Crossf27c5e42020-01-02 09:37:49 -0800451 EmbeddedInterface: &struct{ S string }{
452 S: "abc",
Colin Crosse32cc802016-06-07 12:28:16 -0700453 },
454 Nested: struct {
455 EmbeddedInterface
456 }{
Colin Crossf27c5e42020-01-02 09:37:49 -0800457 EmbeddedInterface: &struct{ S string }{
458 S: "def",
Colin Crosse32cc802016-06-07 12:28:16 -0700459 },
Colin Cross9d1469d2015-11-20 17:03:25 -0800460 },
461 },
462 },
Colin Cross9d1469d2015-11-20 17:03:25 -0800463 },
464
465 // Anonymous struct with name collision
Colin Crossc3d73122016-08-05 17:19:36 -0700466 {
Colin Cross461d9502020-01-23 22:33:21 -0800467 name: "embedded name collision",
Colin Crossc3d73122016-08-05 17:19:36 -0700468 input: `
469 m {
Colin Crossf27c5e42020-01-02 09:37:49 -0800470 s: "abc",
Colin Crossc3d73122016-08-05 17:19:36 -0700471 nested: {
Colin Crossf27c5e42020-01-02 09:37:49 -0800472 s: "def",
Colin Crossc3d73122016-08-05 17:19:36 -0700473 },
474 }
Colin Cross9d1469d2015-11-20 17:03:25 -0800475 `,
Colin Crossc3d73122016-08-05 17:19:36 -0700476 output: []interface{}{
Colin Cross5d57b2d2020-01-27 16:14:31 -0800477 &struct {
Colin Crossf27c5e42020-01-02 09:37:49 -0800478 S string
Colin Cross9d1469d2015-11-20 17:03:25 -0800479 EmbeddedStruct
Colin Crosse32cc802016-06-07 12:28:16 -0700480 Nested struct {
Colin Crossf27c5e42020-01-02 09:37:49 -0800481 S string
Colin Crosse32cc802016-06-07 12:28:16 -0700482 EmbeddedStruct
483 }
Colin Cross9d1469d2015-11-20 17:03:25 -0800484 }{
Colin Crossf27c5e42020-01-02 09:37:49 -0800485 S: "abc",
Colin Cross9d1469d2015-11-20 17:03:25 -0800486 EmbeddedStruct: EmbeddedStruct{
Colin Crossf27c5e42020-01-02 09:37:49 -0800487 S: "abc",
Colin Crosse32cc802016-06-07 12:28:16 -0700488 },
489 Nested: struct {
Colin Crossf27c5e42020-01-02 09:37:49 -0800490 S string
Colin Crosse32cc802016-06-07 12:28:16 -0700491 EmbeddedStruct
492 }{
Colin Crossf27c5e42020-01-02 09:37:49 -0800493 S: "def",
Colin Crosse32cc802016-06-07 12:28:16 -0700494 EmbeddedStruct: EmbeddedStruct{
Colin Crossf27c5e42020-01-02 09:37:49 -0800495 S: "def",
Colin Crosse32cc802016-06-07 12:28:16 -0700496 },
Colin Cross9d1469d2015-11-20 17:03:25 -0800497 },
498 },
499 },
Colin Cross9d1469d2015-11-20 17:03:25 -0800500 },
501
502 // Anonymous interface with name collision
Colin Crossc3d73122016-08-05 17:19:36 -0700503 {
Colin Cross461d9502020-01-23 22:33:21 -0800504 name: "embeded interface name collision",
Colin Crossc3d73122016-08-05 17:19:36 -0700505 input: `
506 m {
Colin Crossf27c5e42020-01-02 09:37:49 -0800507 s: "abc",
Colin Crossc3d73122016-08-05 17:19:36 -0700508 nested: {
Colin Crossf27c5e42020-01-02 09:37:49 -0800509 s: "def",
Colin Crossc3d73122016-08-05 17:19:36 -0700510 },
511 }
Colin Cross9d1469d2015-11-20 17:03:25 -0800512 `,
Colin Crossc3d73122016-08-05 17:19:36 -0700513 output: []interface{}{
Colin Cross5d57b2d2020-01-27 16:14:31 -0800514 &struct {
Colin Crossf27c5e42020-01-02 09:37:49 -0800515 S string
Colin Cross9d1469d2015-11-20 17:03:25 -0800516 EmbeddedInterface
Colin Crosse32cc802016-06-07 12:28:16 -0700517 Nested struct {
Colin Crossf27c5e42020-01-02 09:37:49 -0800518 S string
Colin Crosse32cc802016-06-07 12:28:16 -0700519 EmbeddedInterface
520 }
Colin Cross9d1469d2015-11-20 17:03:25 -0800521 }{
Colin Crossf27c5e42020-01-02 09:37:49 -0800522 S: "abc",
523 EmbeddedInterface: &struct{ S string }{
524 S: "abc",
Colin Crosse32cc802016-06-07 12:28:16 -0700525 },
526 Nested: struct {
Colin Crossf27c5e42020-01-02 09:37:49 -0800527 S string
Colin Crosse32cc802016-06-07 12:28:16 -0700528 EmbeddedInterface
529 }{
Colin Crossf27c5e42020-01-02 09:37:49 -0800530 S: "def",
531 EmbeddedInterface: &struct{ S string }{
532 S: "def",
Colin Crosse32cc802016-06-07 12:28:16 -0700533 },
Colin Cross9d1469d2015-11-20 17:03:25 -0800534 },
535 },
536 },
Colin Cross9d1469d2015-11-20 17:03:25 -0800537 },
Colin Crosse32cc802016-06-07 12:28:16 -0700538
539 // Variables
Colin Crossc3d73122016-08-05 17:19:36 -0700540 {
Colin Cross461d9502020-01-23 22:33:21 -0800541 name: "variables",
Colin Crossc3d73122016-08-05 17:19:36 -0700542 input: `
543 list = ["abc"]
544 string = "def"
545 list_with_variable = [string]
Sasha Smundakde4d9f92020-03-03 17:36:00 -0800546 struct_value = { name: "foo" }
Colin Crossc3d73122016-08-05 17:19:36 -0700547 m {
Colin Crossf27c5e42020-01-02 09:37:49 -0800548 s: string,
Colin Crossc3d73122016-08-05 17:19:36 -0700549 list: list,
550 list2: list_with_variable,
Sasha Smundakde4d9f92020-03-03 17:36:00 -0800551 structattr: struct_value,
Colin Crossc3d73122016-08-05 17:19:36 -0700552 }
Colin Crosse32cc802016-06-07 12:28:16 -0700553 `,
Colin Crossc3d73122016-08-05 17:19:36 -0700554 output: []interface{}{
Colin Cross5d57b2d2020-01-27 16:14:31 -0800555 &struct {
Sasha Smundakde4d9f92020-03-03 17:36:00 -0800556 S string
557 List []string
558 List2 []string
559 Structattr struct {
560 Name string
561 }
Colin Crosse32cc802016-06-07 12:28:16 -0700562 }{
Colin Crossf27c5e42020-01-02 09:37:49 -0800563 S: "def",
Colin Crosse32cc802016-06-07 12:28:16 -0700564 List: []string{"abc"},
565 List2: []string{"def"},
Sasha Smundakde4d9f92020-03-03 17:36:00 -0800566 Structattr: struct {
567 Name string
568 }{
569 Name: "foo",
570 },
Colin Crosse32cc802016-06-07 12:28:16 -0700571 },
572 },
Colin Crosse32cc802016-06-07 12:28:16 -0700573 },
574
575 // Multiple property structs
Colin Crossc3d73122016-08-05 17:19:36 -0700576 {
Colin Cross461d9502020-01-23 22:33:21 -0800577 name: "multiple",
Colin Crossc3d73122016-08-05 17:19:36 -0700578 input: `
579 m {
580 nested: {
Colin Crossf27c5e42020-01-02 09:37:49 -0800581 s: "abc",
Colin Crossc3d73122016-08-05 17:19:36 -0700582 }
Colin Crosse32cc802016-06-07 12:28:16 -0700583 }
Colin Crosse32cc802016-06-07 12:28:16 -0700584 `,
Colin Crossc3d73122016-08-05 17:19:36 -0700585 output: []interface{}{
Colin Cross5d57b2d2020-01-27 16:14:31 -0800586 &struct {
Colin Crosse32cc802016-06-07 12:28:16 -0700587 Nested struct {
Colin Crossf27c5e42020-01-02 09:37:49 -0800588 S string
Colin Crosse32cc802016-06-07 12:28:16 -0700589 }
590 }{
Colin Crossf27c5e42020-01-02 09:37:49 -0800591 Nested: struct{ S string }{
592 S: "abc",
Colin Crosse32cc802016-06-07 12:28:16 -0700593 },
594 },
Colin Cross5d57b2d2020-01-27 16:14:31 -0800595 &struct {
Colin Crosse32cc802016-06-07 12:28:16 -0700596 Nested struct {
Colin Crossf27c5e42020-01-02 09:37:49 -0800597 S string
Colin Crosse32cc802016-06-07 12:28:16 -0700598 }
599 }{
Colin Crossf27c5e42020-01-02 09:37:49 -0800600 Nested: struct{ S string }{
601 S: "abc",
Colin Crosse32cc802016-06-07 12:28:16 -0700602 },
603 },
Colin Cross5d57b2d2020-01-27 16:14:31 -0800604 &struct {
Colin Crosse32cc802016-06-07 12:28:16 -0700605 }{},
606 },
Colin Crossc3d73122016-08-05 17:19:36 -0700607 },
608
609 // Nil pointer to struct
610 {
Colin Cross461d9502020-01-23 22:33:21 -0800611 name: "nil struct pointer",
Colin Crossc3d73122016-08-05 17:19:36 -0700612 input: `
613 m {
614 nested: {
Colin Crossf27c5e42020-01-02 09:37:49 -0800615 s: "abc",
Colin Crossc3d73122016-08-05 17:19:36 -0700616 }
617 }
618 `,
619 output: []interface{}{
Colin Cross5d57b2d2020-01-27 16:14:31 -0800620 &struct {
Colin Crossc3d73122016-08-05 17:19:36 -0700621 Nested *struct {
Colin Crossf27c5e42020-01-02 09:37:49 -0800622 S string
Colin Crossc3d73122016-08-05 17:19:36 -0700623 }
624 }{
Colin Crossf27c5e42020-01-02 09:37:49 -0800625 Nested: &struct{ S string }{
626 S: "abc",
Colin Crossc3d73122016-08-05 17:19:36 -0700627 },
628 },
629 },
630 empty: []interface{}{
631 &struct {
632 Nested *struct {
Colin Crossf27c5e42020-01-02 09:37:49 -0800633 S string
Colin Crossc3d73122016-08-05 17:19:36 -0700634 }
635 }{},
636 },
637 },
638
639 // Interface containing nil pointer to struct
640 {
Colin Cross461d9502020-01-23 22:33:21 -0800641 name: "interface nil struct pointer",
Colin Crossc3d73122016-08-05 17:19:36 -0700642 input: `
643 m {
644 nested: {
Colin Crossf27c5e42020-01-02 09:37:49 -0800645 s: "abc",
Colin Crossc3d73122016-08-05 17:19:36 -0700646 }
647 }
648 `,
649 output: []interface{}{
Colin Cross5d57b2d2020-01-27 16:14:31 -0800650 &struct {
Colin Crossc3d73122016-08-05 17:19:36 -0700651 Nested interface{}
652 }{
653 Nested: &EmbeddedStruct{
Colin Crossf27c5e42020-01-02 09:37:49 -0800654 S: "abc",
Colin Crossc3d73122016-08-05 17:19:36 -0700655 },
656 },
657 },
658 empty: []interface{}{
659 &struct {
660 Nested interface{}
661 }{
662 Nested: (*EmbeddedStruct)(nil),
663 },
664 },
Colin Crosse32cc802016-06-07 12:28:16 -0700665 },
Colin Cross05b36072017-07-28 17:51:37 -0700666
667 // Factory set properties
668 {
Colin Cross461d9502020-01-23 22:33:21 -0800669 name: "factory properties",
Colin Cross05b36072017-07-28 17:51:37 -0700670 input: `
671 m {
672 string: "abc",
673 string_ptr: "abc",
674 bool: false,
675 bool_ptr: false,
676 list: ["a", "b", "c"],
677 }
678 `,
679 output: []interface{}{
Colin Cross5d57b2d2020-01-27 16:14:31 -0800680 &struct {
Colin Cross05b36072017-07-28 17:51:37 -0700681 String string
682 String_ptr *string
683 Bool bool
684 Bool_ptr *bool
685 List []string
686 }{
687 String: "012abc",
Colin Crossf27c5e42020-01-02 09:37:49 -0800688 String_ptr: StringPtr("abc"),
Colin Cross05b36072017-07-28 17:51:37 -0700689 Bool: true,
Colin Crossf27c5e42020-01-02 09:37:49 -0800690 Bool_ptr: BoolPtr(false),
Colin Cross05b36072017-07-28 17:51:37 -0700691 List: []string{"0", "1", "2", "a", "b", "c"},
692 },
693 },
694 empty: []interface{}{
695 &struct {
696 String string
697 String_ptr *string
698 Bool bool
699 Bool_ptr *bool
700 List []string
701 }{
702 String: "012",
Colin Crossf27c5e42020-01-02 09:37:49 -0800703 String_ptr: StringPtr("012"),
Colin Cross05b36072017-07-28 17:51:37 -0700704 Bool: true,
Colin Crossf27c5e42020-01-02 09:37:49 -0800705 Bool_ptr: BoolPtr(true),
Colin Cross05b36072017-07-28 17:51:37 -0700706 List: []string{"0", "1", "2"},
707 },
708 },
709 },
Colin Cross3bbbdf32020-02-05 13:45:11 -0800710 // Captitalized property
711 {
712 input: `
713 m {
714 CAPITALIZED: "foo",
715 }
716 `,
717 output: []interface{}{
718 &struct {
719 CAPITALIZED string
720 }{
721 CAPITALIZED: "foo",
722 },
723 },
724 },
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700725}
726
727func TestUnpackProperties(t *testing.T) {
728 for _, testCase := range validUnpackTestCases {
Colin Cross461d9502020-01-23 22:33:21 -0800729 t.Run(testCase.name, func(t *testing.T) {
730 r := bytes.NewBufferString(testCase.input)
731 file, errs := parser.ParseAndEval("", r, parser.NewScope(nil))
732 if len(errs) != 0 {
Colin Crosse32cc802016-06-07 12:28:16 -0700733 t.Errorf("test case: %s", testCase.input)
Colin Cross461d9502020-01-23 22:33:21 -0800734 t.Errorf("unexpected parse errors:")
Colin Crosse32cc802016-06-07 12:28:16 -0700735 for _, err := range errs {
736 t.Errorf(" %s", err)
737 }
738 t.FailNow()
Colin Crosse32cc802016-06-07 12:28:16 -0700739 }
740
Colin Cross461d9502020-01-23 22:33:21 -0800741 for _, def := range file.Defs {
742 module, ok := def.(*parser.Module)
743 if !ok {
744 continue
745 }
Colin Crosse32cc802016-06-07 12:28:16 -0700746
Colin Cross461d9502020-01-23 22:33:21 -0800747 var output []interface{}
748 if len(testCase.empty) > 0 {
749 for _, p := range testCase.empty {
750 output = append(output, CloneProperties(reflect.ValueOf(p)).Interface())
751 }
752 } else {
753 for _, p := range testCase.output {
754 output = append(output, CloneEmptyProperties(reflect.ValueOf(p)).Interface())
755 }
756 }
757
758 _, errs = UnpackProperties(module.Properties, output...)
759 if len(errs) != 0 && len(testCase.errs) == 0 {
Colin Crosse32cc802016-06-07 12:28:16 -0700760 t.Errorf("test case: %s", testCase.input)
Colin Cross461d9502020-01-23 22:33:21 -0800761 t.Errorf("unexpected unpack errors:")
762 for _, err := range errs {
763 t.Errorf(" %s", err)
764 }
765 t.FailNow()
766 } else if !reflect.DeepEqual(errs, testCase.errs) {
767 t.Errorf("test case: %s", testCase.input)
768 t.Errorf("incorrect errors:")
769 t.Errorf(" expected: %+v", testCase.errs)
770 t.Errorf(" got: %+v", errs)
771 }
772
773 if len(output) != len(testCase.output) {
774 t.Fatalf("incorrect number of property structs, expected %d got %d",
775 len(testCase.output), len(output))
776 }
777
778 for i := range output {
779 got := reflect.ValueOf(output[i]).Interface()
780 if !reflect.DeepEqual(got, testCase.output[i]) {
781 t.Errorf("test case: %s", testCase.input)
782 t.Errorf("incorrect output:")
783 t.Errorf(" expected: %+v", testCase.output[i])
784 t.Errorf(" got: %+v", got)
785 }
Colin Crosse32cc802016-06-07 12:28:16 -0700786 }
787 }
Colin Cross461d9502020-01-23 22:33:21 -0800788 })
789 }
790}
791
792func TestUnpackErrors(t *testing.T) {
793 testCases := []struct {
794 name string
795 input string
796 output []interface{}
797 errors []string
798 }{
799 {
800 name: "missing",
801 input: `
802 m {
803 missing: true,
804 }
805 `,
806 output: []interface{}{},
807 errors: []string{`<input>:3:13: unrecognized property "missing"`},
808 },
809 {
810 name: "missing nested",
811 input: `
812 m {
813 nested: {
814 missing: true,
815 },
816 }
817 `,
818 output: []interface{}{
819 &struct {
820 Nested struct{}
821 }{},
822 },
823 errors: []string{`<input>:4:14: unrecognized property "nested.missing"`},
824 },
825 {
826 name: "mutated",
827 input: `
828 m {
829 mutated: true,
830 }
831 `,
832 output: []interface{}{
833 &struct {
834 Mutated bool `blueprint:"mutated"`
835 }{},
836 },
837 errors: []string{`<input>:3:13: mutated field mutated cannot be set in a Blueprint file`},
838 },
839 {
840 name: "nested mutated",
841 input: `
842 m {
843 nested: {
844 mutated: true,
845 },
846 }
847 `,
848 output: []interface{}{
849 &struct {
850 Nested struct {
851 Mutated bool `blueprint:"mutated"`
852 }
853 }{},
854 },
855 errors: []string{`<input>:4:14: mutated field nested.mutated cannot be set in a Blueprint file`},
856 },
857 {
858 name: "duplicate",
859 input: `
860 m {
861 exists: true,
862 exists: true,
863 }
864 `,
865 output: []interface{}{
866 &struct {
867 Exists bool
868 }{},
869 },
870 errors: []string{
871 `<input>:4:12: property "exists" already defined`,
872 `<input>:3:12: <-- previous definition here`,
873 },
874 },
875 {
876 name: "nested duplicate",
877 input: `
878 m {
879 nested: {
880 exists: true,
881 exists: true,
882 },
883 }
884 `,
885 output: []interface{}{
886 &struct {
887 Nested struct {
888 Exists bool
889 }
890 }{},
891 },
892 errors: []string{
893 `<input>:5:13: property "nested.exists" already defined`,
894 `<input>:4:13: <-- previous definition here`,
895 },
896 },
Colin Cross3adb2402021-01-15 20:09:51 -0800897 {
898 name: "wrong type",
899 input: `
900 m {
901 int: "foo",
902 }
903 `,
904 output: []interface{}{
905 &struct {
906 Int *int64
907 }{},
908 },
909 errors: []string{
910 `<input>:3:11: can't assign string value to int64 property "int"`,
911 },
912 },
913 {
914 name: "wrong type for map",
915 input: `
916 m {
917 map: "foo",
918 }
919 `,
920 output: []interface{}{
921 &struct {
922 Map struct {
923 S string
924 }
925 }{},
926 },
927 errors: []string{
928 `<input>:3:11: can't assign string value to map property "map"`,
929 },
930 },
931 {
932 name: "wrong type for list",
933 input: `
934 m {
935 list: "foo",
936 }
937 `,
938 output: []interface{}{
939 &struct {
940 List []string
941 }{},
942 },
943 errors: []string{
944 `<input>:3:12: can't assign string value to list property "list"`,
945 },
946 },
947 {
948 name: "wrong type for list of maps",
949 input: `
950 m {
951 map_list: "foo",
952 }
953 `,
954 output: []interface{}{
955 &struct {
956 Map_list []struct {
957 S string
958 }
959 }{},
960 },
961 errors: []string{
962 `<input>:3:16: can't assign string value to list property "map_list"`,
963 },
964 },
Colin Cross461d9502020-01-23 22:33:21 -0800965 }
966
967 for _, testCase := range testCases {
968 t.Run(testCase.name, func(t *testing.T) {
969 r := bytes.NewBufferString(testCase.input)
970 file, errs := parser.ParseAndEval("", r, parser.NewScope(nil))
971 if len(errs) != 0 {
972 t.Errorf("test case: %s", testCase.input)
973 t.Errorf("unexpected parse errors:")
974 for _, err := range errs {
975 t.Errorf(" %s", err)
976 }
977 t.FailNow()
978 }
979
980 for _, def := range file.Defs {
981 module, ok := def.(*parser.Module)
982 if !ok {
983 continue
984 }
985
986 var output []interface{}
987 for _, p := range testCase.output {
988 output = append(output, CloneEmptyProperties(reflect.ValueOf(p)).Interface())
989 }
990
991 _, errs = UnpackProperties(module.Properties, output...)
992
993 printErrors := false
994 for _, expectedErr := range testCase.errors {
995 foundError := false
996 for _, err := range errs {
997 if err.Error() == expectedErr {
998 foundError = true
999 }
1000 }
1001 if !foundError {
1002 t.Errorf("expected error %s", expectedErr)
1003 printErrors = true
1004 }
1005 }
1006 if printErrors {
1007 t.Errorf("got errors:")
1008 for _, err := range errs {
1009 t.Errorf(" %s", err.Error())
1010 }
1011 }
1012 }
1013 })
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001014 }
1015}
Colin Cross017ed2e2016-05-31 15:53:32 -07001016
Sasha Smundak29fdcad2020-02-11 22:39:47 -08001017func BenchmarkUnpackProperties(b *testing.B) {
1018 run := func(b *testing.B, props []interface{}, input string) {
1019 b.ReportAllocs()
1020 b.StopTimer()
1021 r := bytes.NewBufferString(input)
1022 file, errs := parser.ParseAndEval("", r, parser.NewScope(nil))
1023 if len(errs) != 0 {
1024 b.Errorf("test case: %s", input)
1025 b.Errorf("unexpected parse errors:")
1026 for _, err := range errs {
1027 b.Errorf(" %s", err)
1028 }
1029 b.FailNow()
1030 }
1031
1032 for i := 0; i < b.N; i++ {
1033 for _, def := range file.Defs {
1034 module, ok := def.(*parser.Module)
1035 if !ok {
1036 continue
1037 }
1038
1039 var output []interface{}
1040 for _, p := range props {
1041 output = append(output, CloneProperties(reflect.ValueOf(p)).Interface())
1042 }
1043
1044 b.StartTimer()
1045 _, errs = UnpackProperties(module.Properties, output...)
1046 b.StopTimer()
1047 if len(errs) > 0 {
1048 b.Errorf("unexpected unpack errors:")
1049 for _, err := range errs {
1050 b.Errorf(" %s", err)
1051 }
1052 }
1053 }
1054 }
Colin Cross017ed2e2016-05-31 15:53:32 -07001055 }
Sasha Smundak29fdcad2020-02-11 22:39:47 -08001056
1057 b.Run("basic", func(b *testing.B) {
1058 props := []interface{}{
1059 &struct {
1060 Nested struct {
1061 S string
1062 }
1063 }{},
1064 }
1065 bp := `
1066 m {
1067 nested: {
1068 s: "abc",
1069 },
1070 }
1071 `
1072 run(b, props, bp)
1073 })
1074
1075 b.Run("interface", func(b *testing.B) {
1076 props := []interface{}{
1077 &struct {
1078 Nested interface{}
1079 }{
1080 Nested: (*struct {
1081 S string
1082 })(nil),
1083 },
1084 }
1085 bp := `
1086 m {
1087 nested: {
1088 s: "abc",
1089 },
1090 }
1091 `
1092 run(b, props, bp)
1093 })
1094
1095 b.Run("many", func(b *testing.B) {
1096 props := []interface{}{
1097 &struct {
1098 A *string
1099 B *string
1100 C *string
1101 D *string
1102 E *string
1103 F *string
1104 G *string
1105 H *string
1106 I *string
1107 J *string
1108 }{},
1109 }
1110 bp := `
1111 m {
1112 a: "a",
1113 b: "b",
1114 c: "c",
1115 d: "d",
1116 e: "e",
1117 f: "f",
1118 g: "g",
1119 h: "h",
1120 i: "i",
1121 j: "j",
1122 }
1123 `
1124 run(b, props, bp)
1125 })
1126
1127 b.Run("deep", func(b *testing.B) {
1128 props := []interface{}{
1129 &struct {
1130 Nested struct {
1131 Nested struct {
1132 Nested struct {
1133 Nested struct {
1134 Nested struct {
1135 Nested struct {
1136 Nested struct {
1137 Nested struct {
1138 Nested struct {
1139 Nested struct {
1140 S string
1141 }
1142 }
1143 }
1144 }
1145 }
1146 }
1147 }
1148 }
1149 }
1150 }
1151 }{},
1152 }
1153 bp := `
1154 m {
1155 nested: { nested: { nested: { nested: { nested: {
1156 nested: { nested: { nested: { nested: { nested: {
1157 s: "abc",
1158 }, }, }, }, },
1159 }, }, }, }, },
1160 }
1161 `
1162 run(b, props, bp)
1163 })
1164
1165 b.Run("mix", func(b *testing.B) {
1166 props := []interface{}{
1167 &struct {
1168 Name string
1169 Flag bool
1170 Settings []string
1171 Perarch *struct {
1172 Arm string
1173 Arm64 string
1174 }
1175 Configvars []struct {
1176 Name string
1177 Values []string
1178 }
1179 }{},
1180 }
1181 bp := `
1182 m {
1183 name: "mymodule",
1184 flag: true,
1185 settings: ["foo1", "foo2", "foo3",],
1186 perarch: {
1187 arm: "32",
1188 arm64: "64",
1189 },
1190 configvars: [
1191 { name: "var1", values: ["var1:1", "var1:2", ], },
1192 { name: "var2", values: ["var2:1", "var2:2", ], },
1193 ],
1194 }
1195 `
1196 run(b, props, bp)
1197 })
Colin Cross017ed2e2016-05-31 15:53:32 -07001198}