9d1469d559
Allow property structs to contain anonymous embedded structs and interfaces. Properties in an anonymous embedded struct or interface are treated as if they were properties in the embedding struct.
401 lines
6.2 KiB
Go
401 lines
6.2 KiB
Go
// Copyright 2014 Google Inc. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package blueprint
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"reflect"
|
|
"testing"
|
|
"text/scanner"
|
|
|
|
"github.com/google/blueprint/parser"
|
|
"github.com/google/blueprint/proptools"
|
|
)
|
|
|
|
var validUnpackTestCases = []struct {
|
|
input string
|
|
output interface{}
|
|
errs []error
|
|
}{
|
|
{`
|
|
m {
|
|
name: "abc",
|
|
blank: "",
|
|
}
|
|
`,
|
|
struct {
|
|
Name *string
|
|
Blank *string
|
|
Unset *string
|
|
}{
|
|
Name: proptools.StringPtr("abc"),
|
|
Blank: proptools.StringPtr(""),
|
|
Unset: nil,
|
|
},
|
|
nil,
|
|
},
|
|
|
|
{`
|
|
m {
|
|
name: "abc",
|
|
}
|
|
`,
|
|
struct {
|
|
Name string
|
|
}{
|
|
Name: "abc",
|
|
},
|
|
nil,
|
|
},
|
|
|
|
{`
|
|
m {
|
|
isGood: true,
|
|
}
|
|
`,
|
|
struct {
|
|
IsGood bool
|
|
}{
|
|
IsGood: true,
|
|
},
|
|
nil,
|
|
},
|
|
|
|
{`
|
|
m {
|
|
isGood: true,
|
|
isBad: false,
|
|
}
|
|
`,
|
|
struct {
|
|
IsGood *bool
|
|
IsBad *bool
|
|
IsUgly *bool
|
|
}{
|
|
IsGood: proptools.BoolPtr(true),
|
|
IsBad: proptools.BoolPtr(false),
|
|
IsUgly: nil,
|
|
},
|
|
nil,
|
|
},
|
|
|
|
{`
|
|
m {
|
|
stuff: ["asdf", "jkl;", "qwert",
|
|
"uiop", "bnm,"],
|
|
empty: []
|
|
}
|
|
`,
|
|
struct {
|
|
Stuff []string
|
|
Empty []string
|
|
Nil []string
|
|
}{
|
|
Stuff: []string{"asdf", "jkl;", "qwert", "uiop", "bnm,"},
|
|
Empty: []string{},
|
|
Nil: nil,
|
|
},
|
|
nil,
|
|
},
|
|
|
|
{`
|
|
m {
|
|
nested: {
|
|
name: "abc",
|
|
}
|
|
}
|
|
`,
|
|
struct {
|
|
Nested struct {
|
|
Name string
|
|
}
|
|
}{
|
|
Nested: struct{ Name string }{
|
|
Name: "abc",
|
|
},
|
|
},
|
|
nil,
|
|
},
|
|
|
|
{`
|
|
m {
|
|
nested: {
|
|
name: "def",
|
|
}
|
|
}
|
|
`,
|
|
struct {
|
|
Nested interface{}
|
|
}{
|
|
Nested: &struct{ Name string }{
|
|
Name: "def",
|
|
},
|
|
},
|
|
nil,
|
|
},
|
|
|
|
{`
|
|
m {
|
|
nested: {
|
|
foo: "abc",
|
|
},
|
|
bar: false,
|
|
baz: ["def", "ghi"],
|
|
}
|
|
`,
|
|
struct {
|
|
Nested struct {
|
|
Foo string
|
|
}
|
|
Bar bool
|
|
Baz []string
|
|
}{
|
|
Nested: struct{ Foo string }{
|
|
Foo: "abc",
|
|
},
|
|
Bar: false,
|
|
Baz: []string{"def", "ghi"},
|
|
},
|
|
nil,
|
|
},
|
|
|
|
{`
|
|
m {
|
|
nested: {
|
|
foo: "abc",
|
|
},
|
|
bar: false,
|
|
baz: ["def", "ghi"],
|
|
}
|
|
`,
|
|
struct {
|
|
Nested struct {
|
|
Foo string `allowNested:"true"`
|
|
} `blueprint:"filter(allowNested:\"true\")"`
|
|
Bar bool
|
|
Baz []string
|
|
}{
|
|
Nested: struct {
|
|
Foo string `allowNested:"true"`
|
|
}{
|
|
Foo: "abc",
|
|
},
|
|
Bar: false,
|
|
Baz: []string{"def", "ghi"},
|
|
},
|
|
nil,
|
|
},
|
|
|
|
{`
|
|
m {
|
|
nested: {
|
|
foo: "abc",
|
|
},
|
|
bar: false,
|
|
baz: ["def", "ghi"],
|
|
}
|
|
`,
|
|
struct {
|
|
Nested struct {
|
|
Foo string
|
|
} `blueprint:"filter(allowNested:\"true\")"`
|
|
Bar bool
|
|
Baz []string
|
|
}{
|
|
Nested: struct{ Foo string }{
|
|
Foo: "",
|
|
},
|
|
Bar: false,
|
|
Baz: []string{"def", "ghi"},
|
|
},
|
|
[]error{
|
|
&Error{
|
|
Err: fmt.Errorf("filtered field nested.foo cannot be set in a Blueprint file"),
|
|
Pos: scanner.Position{"", 27, 4, 8},
|
|
},
|
|
},
|
|
},
|
|
|
|
// Anonymous struct
|
|
{`
|
|
m {
|
|
name: "abc",
|
|
nested: {
|
|
name: "def",
|
|
},
|
|
}
|
|
`,
|
|
struct {
|
|
EmbeddedStruct
|
|
Nested struct {
|
|
EmbeddedStruct
|
|
}
|
|
}{
|
|
EmbeddedStruct: EmbeddedStruct{
|
|
Name: "abc",
|
|
},
|
|
Nested: struct {
|
|
EmbeddedStruct
|
|
}{
|
|
EmbeddedStruct: EmbeddedStruct{
|
|
Name: "def",
|
|
},
|
|
},
|
|
},
|
|
nil,
|
|
},
|
|
|
|
// Anonymous interface
|
|
{`
|
|
m {
|
|
name: "abc",
|
|
nested: {
|
|
name: "def",
|
|
},
|
|
}
|
|
`,
|
|
struct {
|
|
EmbeddedInterface
|
|
Nested struct {
|
|
EmbeddedInterface
|
|
}
|
|
}{
|
|
EmbeddedInterface: &struct{ Name string }{
|
|
Name: "abc",
|
|
},
|
|
Nested: struct {
|
|
EmbeddedInterface
|
|
}{
|
|
EmbeddedInterface: &struct{ Name string }{
|
|
Name: "def",
|
|
},
|
|
},
|
|
},
|
|
nil,
|
|
},
|
|
|
|
// Anonymous struct with name collision
|
|
{`
|
|
m {
|
|
name: "abc",
|
|
nested: {
|
|
name: "def",
|
|
},
|
|
}
|
|
`,
|
|
struct {
|
|
Name string
|
|
EmbeddedStruct
|
|
Nested struct {
|
|
Name string
|
|
EmbeddedStruct
|
|
}
|
|
}{
|
|
Name: "abc",
|
|
EmbeddedStruct: EmbeddedStruct{
|
|
Name: "abc",
|
|
},
|
|
Nested: struct {
|
|
Name string
|
|
EmbeddedStruct
|
|
}{
|
|
Name: "def",
|
|
EmbeddedStruct: EmbeddedStruct{
|
|
Name: "def",
|
|
},
|
|
},
|
|
},
|
|
nil,
|
|
},
|
|
|
|
// Anonymous interface with name collision
|
|
{`
|
|
m {
|
|
name: "abc",
|
|
nested: {
|
|
name: "def",
|
|
},
|
|
}
|
|
`,
|
|
struct {
|
|
Name string
|
|
EmbeddedInterface
|
|
Nested struct {
|
|
Name string
|
|
EmbeddedInterface
|
|
}
|
|
}{
|
|
Name: "abc",
|
|
EmbeddedInterface: &struct{ Name string }{
|
|
Name: "abc",
|
|
},
|
|
Nested: struct {
|
|
Name string
|
|
EmbeddedInterface
|
|
}{
|
|
Name: "def",
|
|
EmbeddedInterface: &struct{ Name string }{
|
|
Name: "def",
|
|
},
|
|
},
|
|
},
|
|
nil,
|
|
},
|
|
}
|
|
|
|
type EmbeddedStruct struct{ Name string }
|
|
type EmbeddedInterface interface{}
|
|
|
|
func TestUnpackProperties(t *testing.T) {
|
|
for _, testCase := range validUnpackTestCases {
|
|
r := bytes.NewBufferString(testCase.input)
|
|
file, errs := parser.Parse("", r, nil)
|
|
if len(errs) != 0 {
|
|
t.Errorf("test case: %s", testCase.input)
|
|
t.Errorf("unexpected parse errors:")
|
|
for _, err := range errs {
|
|
t.Errorf(" %s", err)
|
|
}
|
|
t.FailNow()
|
|
}
|
|
|
|
module := file.Defs[0].(*parser.Module)
|
|
properties := proptools.CloneProperties(reflect.ValueOf(testCase.output))
|
|
proptools.ZeroProperties(properties.Elem())
|
|
_, errs = unpackProperties(module.Properties, properties.Interface())
|
|
if len(errs) != 0 && len(testCase.errs) == 0 {
|
|
t.Errorf("test case: %s", testCase.input)
|
|
t.Errorf("unexpected unpack errors:")
|
|
for _, err := range errs {
|
|
t.Errorf(" %s", err)
|
|
}
|
|
t.FailNow()
|
|
} else if !reflect.DeepEqual(errs, testCase.errs) {
|
|
t.Errorf("test case: %s", testCase.input)
|
|
t.Errorf("incorrect errors:")
|
|
t.Errorf(" expected: %+v", testCase.errs)
|
|
t.Errorf(" got: %+v", errs)
|
|
}
|
|
|
|
output := properties.Elem().Interface()
|
|
if !reflect.DeepEqual(output, testCase.output) {
|
|
t.Errorf("test case: %s", testCase.input)
|
|
t.Errorf("incorrect output:")
|
|
t.Errorf(" expected: %+v", testCase.output)
|
|
t.Errorf(" got: %+v", output)
|
|
}
|
|
}
|
|
}
|