Quelle unpack_test.go Sprache: unbekannt
|
|
Spracherkennung für: .go vermutete Sprache: Abap {ABAP[48] Cobol[44] Corba[36]} [Methode: maximale Elemente, drei Dimensionen]
// 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 proptools
import (
"bytes"
"reflect"
"testing"
"text/scanner"
"github.com/google/blueprint/parser"
)
var validUnpackTestCases = []struct {
name string
input string
output [] interface{}
empty [] interface{}
errs []error
}{
{
name: "blank and unset",
input: `
m {
s: "abc",
blank: "",
}
`,
output: [] interface{}{
&struct {
S *string
Blank *string
Unset *string
}{
S: StringPtr( "abc"),
Blank: StringPtr( ""),
Unset: nil,
},
},
},
{
name: "string",
input: `
m {
s: "abc",
}
`,
output: [] interface{}{
&struct {
S string
}{
S: "abc",
},
},
},
{
name: "bool",
input: `
m {
isGood: true,
}
`,
output: [] interface{}{
&struct {
IsGood bool
}{
IsGood: true,
},
},
},
{
name: "boolptr",
input: `
m {
isGood: true,
isBad: false,
}
`,
output: [] interface{}{
&struct {
IsGood *bool
IsBad *bool
IsUgly *bool
}{
IsGood: BoolPtr(true),
IsBad: BoolPtr(false),
IsUgly: nil,
},
},
},
{
name: "slice",
input: `
m {
stuff: [ "asdf", "jkl;", "qwert",
"uiop", "bnm,"],
empty: []
}
`,
output: [] interface{}{
&struct {
Stuff [] string
Empty [] string
Nil [] string
NonString []struct{ S string } `blueprint: "mutated"`
}{
Stuff: [] string{ "asdf", "jkl;", "qwert", "uiop", "bnm,"},
Empty: [] string{},
Nil: nil,
NonString: nil,
},
},
},
{
name: "double nested",
input: `
m {
nested: {
nested: {
s: "abc",
},
},
}
`,
output: [] interface{}{
&struct {
Nested struct {
Nested struct {
S string
}
}
}{
Nested: struct{ Nested struct{ S string } }{
Nested: struct{ S string }{
S: "abc",
},
},
},
},
},
{
name: "nested",
input: `
m {
nested: {
s: "abc",
}
}
`,
output: [] interface{}{
&struct {
Nested struct {
S string
}
}{
Nested: struct{ S string }{
S: "abc",
},
},
},
},
{
name: "nested interface",
input: `
m {
nested: {
s: "def",
}
}
`,
output: [] interface{}{
&struct {
Nested interface{}
}{
Nested: &struct{ S string }{
S: "def",
},
},
},
},
{
name: "mixed",
input: `
m {
nested: {
foo: "abc",
},
bar: false,
baz: [ "def", "ghi"],
}
`,
output: [] interface{}{
&struct {
Nested struct {
Foo string
}
Bar bool
Baz [] string
}{
Nested: struct{ Foo string }{
Foo: "abc",
},
Bar: false,
Baz: [] string{ "def", "ghi"},
},
},
},
{
name: "filter",
input: `
m {
nested: {
foo: "abc",
},
bar: false,
baz: [ "def", "ghi"],
}
`,
output: [] interface{}{
&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"},
},
},
},
// List of maps
{
name: "list of structs",
input: `
m {
mapslist: [
{
foo: "abc",
bar: true,
},
{
foo: "def",
bar: false,
}
],
}
`,
output: [] interface{}{
&struct {
Mapslist []struct {
Foo string
Bar bool
}
}{
Mapslist: []struct {
Foo string
Bar bool
}{
{Foo: "abc", Bar: true},
{Foo: "def", Bar: false},
},
},
},
},
// List of pointers to structs
{
name: "list of pointers to structs",
input: `
m {
mapslist: [
{
foo: "abc",
bar: true,
},
{
foo: "def",
bar: false,
}
],
}
`,
output: [] interface{}{
&struct {
Mapslist [] *struct {
Foo string
Bar bool
}
}{
Mapslist: [] *struct {
Foo string
Bar bool
}{
{Foo: "abc", Bar: true},
{Foo: "def", Bar: false},
},
},
},
},
// List of lists
{
name: "list of lists",
input: `
m {
listoflists: [
[ "abc",],
[ "def",],
],
}
`,
output: [] interface{}{
&struct {
Listoflists [][] string
}{
Listoflists: [][] string{
[] string{ "abc"},
[] string{ "def"},
},
},
},
},
// Multilevel
{
name: "multilevel",
input: `
m {
name: "mymodule",
flag: true,
settings: [ "foo1", "foo2", "foo3",],
perarch: {
arm: "32",
arm64: "64",
},
configvars: [
{ var: "var1", values: [ "1.1", "1.2", ], },
{ var: "var2", values: [ "2.1", ], },
],
}
`,
output: [] interface{}{
&struct {
Name string
Flag bool
Settings [] string
Perarch *struct {
Arm string
Arm64 string
}
Configvars []struct {
Var string
Values [] string
}
}{
Name: "mymodule",
Flag: true,
Settings: [] string{ "foo1", "foo2", "foo3"},
Perarch: &struct {
Arm string
Arm64 string
}{Arm: "32", Arm64: "64"},
Configvars: []struct {
Var string
Values [] string
}{
{Var: "var1", Values: [] string{ "1.1", "1.2"}},
{Var: "var2", Values: [] string{ "2.1"}},
},
},
},
},
// Anonymous struct
{
name: "embedded struct",
input: `
m {
s: "abc",
nested: {
s: "def",
},
}
`,
output: [] interface{}{
&struct {
EmbeddedStruct
Nested struct {
EmbeddedStruct
}
}{
EmbeddedStruct: EmbeddedStruct{
S: "abc",
},
Nested: struct {
EmbeddedStruct
}{
EmbeddedStruct: EmbeddedStruct{
S: "def",
},
},
},
},
},
// Anonymous interface
{
name: "embedded interface",
input: `
m {
s: "abc",
nested: {
s: "def",
},
}
`,
output: [] interface{}{
&struct {
EmbeddedInterface
Nested struct {
EmbeddedInterface
}
}{
EmbeddedInterface: &struct{ S string }{
S: "abc",
},
Nested: struct {
EmbeddedInterface
}{
EmbeddedInterface: &struct{ S string }{
S: "def",
},
},
},
},
},
// Anonymous struct with name collision
{
name: "embedded name collision",
input: `
m {
s: "abc",
nested: {
s: "def",
},
}
`,
output: [] interface{}{
&struct {
S string
EmbeddedStruct
Nested struct {
S string
EmbeddedStruct
}
}{
S: "abc",
EmbeddedStruct: EmbeddedStruct{
S: "abc",
},
Nested: struct {
S string
EmbeddedStruct
}{
S: "def",
EmbeddedStruct: EmbeddedStruct{
S: "def",
},
},
},
},
},
// Anonymous interface with name collision
{
name: "embeded interface name collision",
input: `
m {
s: "abc",
nested: {
s: "def",
},
}
`,
output: [] interface{}{
&struct {
S string
EmbeddedInterface
Nested struct {
S string
EmbeddedInterface
}
}{
S: "abc",
EmbeddedInterface: &struct{ S string }{
S: "abc",
},
Nested: struct {
S string
EmbeddedInterface
}{
S: "def",
EmbeddedInterface: &struct{ S string }{
S: "def",
},
},
},
},
},
// Variables
{
name: "variables",
input: `
list = [ "abc"]
string = "def"
list_with_variable = [ string]
struct_value = { name: "foo" }
m {
s: string,
list: list,
list2: list_with_variable,
structattr: struct_value,
}
`,
output: [] interface{}{
&struct {
S string
List [] string
List2 [] string
Structattr struct {
Name string
}
}{
S: "def",
List: [] string{ "abc"},
List2: [] string{ "def"},
Structattr: struct {
Name string
}{
Name: "foo",
},
},
},
},
// Multiple property structs
{
name: "multiple",
input: `
m {
nested: {
s: "abc",
}
}
`,
output: [] interface{}{
&struct {
Nested struct {
S string
}
}{
Nested: struct{ S string }{
S: "abc",
},
},
&struct {
Nested struct {
S string
}
}{
Nested: struct{ S string }{
S: "abc",
},
},
&struct {
}{},
},
},
// Nil pointer to struct
{
name: "nil struct pointer",
input: `
m {
nested: {
s: "abc",
}
}
`,
output: [] interface{}{
&struct {
Nested *struct {
S string
}
}{
Nested: &struct{ S string }{
S: "abc",
},
},
},
empty: [] interface{}{
&struct {
Nested *struct {
S string
}
}{},
},
},
// Interface containing nil pointer to struct
{
name: "interface nil struct pointer",
input: `
m {
nested: {
s: "abc",
}
}
`,
output: [] interface{}{
&struct {
Nested interface{}
}{
Nested: &EmbeddedStruct{
S: "abc",
},
},
},
empty: [] interface{}{
&struct {
Nested interface{}
}{
Nested: ( *EmbeddedStruct)(nil),
},
},
},
// Factory set properties
{
name: "factory properties",
input: `
m {
string: "abc",
string_ptr: "abc",
bool: false,
bool_ptr: false,
list: [ "a", "b", "c"],
}
`,
output: [] interface{}{
&struct {
String string
String_ptr *string
Bool bool
Bool_ptr *bool
List [] string
}{
String: "012abc",
String_ptr: StringPtr( "abc"),
Bool: true,
Bool_ptr: BoolPtr(false),
List: [] string{ "0", "1", "2", "a", "b", "c"},
},
},
empty: [] interface{}{
&struct {
String string
String_ptr *string
Bool bool
Bool_ptr *bool
List [] string
}{
String: "012",
String_ptr: StringPtr( "012"),
Bool: true,
Bool_ptr: BoolPtr(true),
List: [] string{ "0", "1", "2"},
},
},
},
// Captitalized property
{
input: `
m {
CAPITALIZED: "foo",
}
`,
output: [] interface{}{
&struct {
CAPITALIZED string
}{
CAPITALIZED: "foo",
},
},
},
{
name: "String configurable property that isn't configured",
input: `
m {
foo: "bar"
}
`,
output: [] interface{}{
&struct {
Foo Configurable[ string]
}{
Foo: newConfigurableWithPropertyName(
"foo",
nil,
[]ConfigurableCase[ string]{{
value: &parser. String{
LiteralPos: scanner. Position{
Offset: 17,
Line: 3,
Column: 10,
},
Value: "bar",
},
}},
false,
),
},
},
},
{
name: "Bool configurable property that isn't configured",
input: `
m {
foo: true,
}
`,
output: [] interface{}{
&struct {
Foo Configurable[bool]
}{
Foo: newConfigurableWithPropertyName(
"foo",
nil,
[]ConfigurableCase[bool]{{
value: &parser.Bool{
LiteralPos: scanner. Position{
Offset: 17,
Line: 3,
Column: 10,
},
Value: true,
Token: "true",
},
}},
false,
),
},
},
},
{
name: "Int64 configurable property that isn't configured",
input: `
m {
foo: 1234,
}
`,
output: [] interface{}{
&struct {
Foo Configurable[int64]
}{
Foo: newConfigurableWithPropertyName(
"foo",
nil,
[]ConfigurableCase[int64]{{
value: &parser.Int64{
LiteralPos: scanner. Position{
Offset: 17,
Line: 3,
Column: 10,
},
Value: 1234,
Token: "1234",
},
}},
false,
),
},
},
},
{
name: "String list configurable property that isn't configured",
input: `
m {
foo: [ "a", "b"],
}
`,
output: [] interface{}{
&struct {
Foo Configurable[[] string]
}{
Foo: newConfigurableWithPropertyName(
"foo",
nil,
[]ConfigurableCase[[] string]{{
value: &parser.List{
LBracePos: scanner. Position{
Offset: 17,
Line: 3,
Column: 10,
},
RBracePos: scanner. Position{
Offset: 26,
Line: 3,
Column: 19,
},
Values: []parser.Expression{
&parser. String{
LiteralPos: scanner. Position{
Offset: 18,
Line: 3,
Column: 11,
},
Value: "a",
},
&parser. String{
LiteralPos: scanner. Position{
Offset: 23,
Line: 3,
Column: 16,
},
Value: "b",
},
},
},
}},
false,
),
},
},
},
{
name: "Configurable property",
input: `
m {
foo: select(soong_config_variable( "my_namespace", "my_variable"), {
"a": "a2",
"b": "b2",
default: "c2",
})
}
`,
output: [] interface{}{
&struct {
Foo Configurable[ string]
}{
Foo: newConfigurableWithPropertyName(
"foo",
[]ConfigurableCondition{{
functionName: "soong_config_variable",
args: [] string{
"my_namespace",
"my_variable",
},
}},
[]ConfigurableCase[ string]{
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "a",
}},
value: &parser. String{
LiteralPos: scanner. Position{
Offset: 90,
Line: 4,
Column: 11,
},
Value: "a2",
},
},
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "b",
}},
value: &parser. String{
LiteralPos: scanner. Position{
Offset: 106,
Line: 5,
Column: 11,
},
Value: "b2",
},
},
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeDefault,
}},
value: &parser. String{
LiteralPos: scanner. Position{
Offset: 126,
Line: 6,
Column: 15,
},
Value: "c2",
},
},
},
true,
),
},
},
},
{
name: "Configurable property appending",
input: `
m {
foo: select(soong_config_variable( "my_namespace", "my_variable"), {
"a": "a2",
"b": "b2",
default: "c2",
}) + select(soong_config_variable( "my_namespace", "my_2nd_variable"), {
"d": "d2",
"e": "e2",
default: "f2",
})
}
`,
output: [] interface{}{
&struct {
Foo Configurable[ string]
}{
Foo: func() Configurable[ string] {
result := newConfigurableWithPropertyName(
"foo",
[]ConfigurableCondition{{
functionName: "soong_config_variable",
args: [] string{
"my_namespace",
"my_variable",
},
}},
[]ConfigurableCase[ string]{
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "a",
}},
value: &parser. String{
LiteralPos: scanner. Position{
Offset: 90,
Line: 4,
Column: 11,
},
Value: "a2",
},
},
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "b",
}},
value: &parser. String{
LiteralPos: scanner. Position{
Offset: 106,
Line: 5,
Column: 11,
},
Value: "b2",
},
},
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeDefault,
}},
value: &parser. String{
LiteralPos: scanner. Position{
Offset: 126,
Line: 6,
Column: 15,
},
Value: "c2",
},
},
},
true,
)
result. Append(newConfigurableWithPropertyName(
"",
[]ConfigurableCondition{{
functionName: "soong_config_variable",
args: [] string{
"my_namespace",
"my_2nd_variable",
},
}},
[]ConfigurableCase[ string]{
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "d",
}},
value: &parser. String{
LiteralPos: scanner. Position{
Offset: 218,
Line: 8,
Column: 11,
},
Value: "d2",
},
},
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "e",
}},
value: &parser. String{
LiteralPos: scanner. Position{
Offset: 234,
Line: 9,
Column: 11,
},
Value: "e2",
},
},
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeDefault,
}},
value: &parser. String{
LiteralPos: scanner. Position{
Offset: 254,
Line: 10,
Column: 15,
},
Value: "f2",
},
},
},
true,
))
return result
}(),
},
},
},
{
name: "Unpack variable to configurable property",
input: `
my_string_variable = "asdf"
my_bool_variable = true
m {
foo: my_string_variable,
bar: my_bool_variable,
}
`,
output: [] interface{}{
&struct {
Foo Configurable[ string]
Bar Configurable[bool]
}{
Foo: newConfigurableWithPropertyName(
"foo",
nil,
[]ConfigurableCase[ string]{{
value: &parser. String{
LiteralPos: scanner. Position{
Offset: 25,
Line: 2,
Column: 25,
},
Value: "asdf",
},
}},
false,
),
Bar: newConfigurableWithPropertyName(
"bar",
nil,
[]ConfigurableCase[bool]{{
value: &parser.Bool{
LiteralPos: scanner. Position{
Offset: 54,
Line: 3,
Column: 23,
},
Value: true,
Token: "true",
},
}},
false,
),
},
},
},
}
func TestUnpackProperties(t *testing.T) {
for _, testCase := range validUnpackTestCases {
t. Run(testCase.name, func(t *testing.T) {
r := bytes.NewBufferString(testCase. input)
file, errs := parser.ParseAndEval( "", r, parser.NewScope(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()
}
for _, def := range file.Defs {
module, ok := def.( *parser.Module)
if !ok {
continue
}
var output [] interface{}
if len(testCase.empty) > 0 {
for _, p := range testCase.empty {
output = append(output, CloneProperties(reflect.ValueOf(p)). Interface())
}
} else {
for _, p := range testCase.output {
output = append(output, CloneEmptyProperties(reflect.ValueOf(p)). Interface())
}
}
_, errs = UnpackProperties( module.Properties, output...)
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)
}
if len(output) != len(testCase.output) {
t.Fatalf( "incorrect number of property structs, expected %d got %d",
len(testCase.output), len(output))
}
for i := range output {
got := reflect.ValueOf(output[i]). Interface()
if !reflect.DeepEqual(got, testCase.output[i]) {
t.Errorf( "test case: %s", testCase. input)
t.Errorf( "incorrect output:")
t.Errorf( " expected: %+v", testCase.output[i])
t.Errorf( " got: %+v", got)
}
}
}
})
}
}
func TestUnpackErrors(t *testing.T) {
testCases := []struct {
name string
input string
output [] interface{}
errors [] string
}{
{
name: "missing",
input: `
m {
missing: true,
}
`,
output: [] interface{}{},
errors: [] string{`< input>: 3: 13: unrecognized property "missing"`},
},
{
name: "missing nested",
input: `
m {
nested: {
missing: true,
},
}
`,
output: [] interface{}{
&struct {
Nested struct{}
}{},
},
errors: [] string{`< input>: 4: 14: unrecognized property "nested.missing"`},
},
{
name: "mutated",
input: `
m {
mutated: true,
}
`,
output: [] interface{}{
&struct {
Mutated bool `blueprint: "mutated"`
}{},
},
errors: [] string{`< input>: 3: 13: mutated field mutated cannot be set in a Blueprint file`},
},
{
name: "nested mutated",
input: `
m {
nested: {
mutated: true,
},
}
`,
output: [] interface{}{
&struct {
Nested struct {
Mutated bool `blueprint: "mutated"`
}
}{},
},
errors: [] string{`< input>: 4: 14: mutated field nested.mutated cannot be set in a Blueprint file`},
},
{
name: "duplicate",
input: `
m {
exists: true,
exists: true,
}
`,
output: [] interface{}{
&struct {
Exists bool
}{},
},
errors: [] string{
`< input>: 4: 12: property "exists" already defined`,
`< input>: 3: 12: <-- previous definition here`,
},
},
{
name: "nested duplicate",
input: `
m {
nested: {
exists: true,
exists: true,
},
}
`,
output: [] interface{}{
&struct {
Nested struct {
Exists bool
}
}{},
},
errors: [] string{
`< input>: 5: 13: property "nested.exists" already defined`,
`< input>: 4: 13: <-- previous definition here`,
},
},
{
name: "wrong type",
input: `
m {
int: "foo",
}
`,
output: [] interface{}{
&struct {
Int *int64
}{},
},
errors: [] string{
`< input>: 3: 11: can 't assign string value to int64 property "int"`,
},
},
{
name: "wrong type for map",
input: `
m {
map: "foo",
}
`,
output: [] interface{}{
&struct {
Map struct {
S string
}
}{},
},
errors: [] string{
`< input>: 3: 11: can 't assign string value to map property "map"`,
},
},
{
name: "wrong type for list",
input: `
m {
list: "foo",
}
`,
output: [] interface{}{
&struct {
List [] string
}{},
},
errors: [] string{
`< input>: 3: 12: can 't assign string value to list property "list"`,
},
},
{
name: "wrong type for list of maps",
input: `
m {
map_list: "foo",
}
`,
output: [] interface{}{
&struct {
Map_list []struct {
S string
}
}{},
},
errors: [] string{
`< input>: 3: 16: can 't assign string value to list property "map_list"`,
},
},
{
name: "non-existent property",
input: `
m {
foo: {
foo_prop1: true,
foo_prop2: false,
foo_prop3: true,
},
bar: {
bar_prop: false,
},
baz: true,
exist: false,
}
`,
output: [] interface{}{
&struct {
Foo struct {
Foo_prop1 bool
}
Exist bool
}{},
},
errors: [] string{
`< input>: 5: 16: unrecognized property "foo.foo_prop2"`,
`< input>: 6: 16: unrecognized property "foo.foo_prop3"`,
`< input>: 9: 15: unrecognized property "bar.bar_prop"`,
`< input>: 11: 9: unrecognized property "baz"`,
},
},
{
name: "Select on non-configurable string property",
input: `
m {
foo: select(soong_config_variable( "my_namespace", "my_variable"), {
"a": "b",
default: unset,
}),
}
`,
output: [] interface{}{
&struct {
Foo *string
}{},
},
errors: [] string{
`< input>: 3: 10: can 't assign select statement to non-configurable property "foo". This requires a small soong change to enable in most cases, please file a go/soong-bug if you'd like to use a select statement here`,
},
},
{
name: "Select on non-configurable bool property",
input: `
m {
foo: select(soong_config_variable( "my_namespace", "my_variable"), {
"a": true,
default: unset,
}),
}
`,
output: [] interface{}{
&struct {
Foo *bool
}{},
},
errors: [] string{
`< input>: 3: 10: can 't assign select statement to non-configurable property "foo". This requires a small soong change to enable in most cases, please file a go/soong-bug if you'd like to use a select statement here`,
},
},
{
name: "Select on non-configurable int64 property",
input: `
m {
foo: select(soong_config_variable( "my_namespace", "my_variable"), {
"a": 1234,
default: unset,
}),
}
`,
output: [] interface{}{
&struct {
Foo *int64
}{},
},
errors: [] string{
`< input>: 3: 10: can 't assign select statement to non-configurable property "foo". This requires a small soong change to enable in most cases, please file a go/soong-bug if you'd like to use a select statement here`,
},
},
}
for _, testCase := range testCases {
t. Run(testCase.name, func(t *testing.T) {
r := bytes.NewBufferString(testCase. input)
file, errs := parser.ParseAndEval( "", r, parser.NewScope(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()
}
for _, def := range file.Defs {
module, ok := def.( *parser.Module)
if !ok {
continue
}
var output [] interface{}
for _, p := range testCase.output {
output = append(output, CloneEmptyProperties(reflect.ValueOf(p)). Interface())
}
_, errs = UnpackProperties( module.Properties, output...)
printErrors := false
for _, expectedErr := range testCase.errors {
foundError := false
for _, err := range errs {
if err.Error() == expectedErr {
foundError = true
}
}
if !foundError {
t.Errorf( "expected error %s", expectedErr)
printErrors = true
}
}
if printErrors {
t.Errorf( "got errors:")
for _, err := range errs {
t.Errorf( " %s", err.Error())
}
}
}
})
}
}
func BenchmarkUnpackProperties(b *testing.B) {
run := func(b *testing.B, props []interface{}, input string) {
b.ReportAllocs()
b.StopTimer()
r := bytes.NewBufferString( input)
file, errs := parser.ParseAndEval( "", r, parser.NewScope(nil))
if len(errs) != 0 {
b.Errorf( "test case: %s", input)
b.Errorf( "unexpected parse errors:")
for _, err := range errs {
b.Errorf( " %s", err)
}
b.FailNow()
}
for i := 0; i < b.N; i++ {
for _, def := range file.Defs {
module, ok := def.( *parser.Module)
if !ok {
continue
}
var output [] interface{}
for _, p := range props {
output = append(output, CloneProperties(reflect.ValueOf(p)). Interface())
}
b.StartTimer()
_, errs = UnpackProperties( module.Properties, output...)
b.StopTimer()
if len(errs) > 0 {
b.Errorf( "unexpected unpack errors:")
for _, err := range errs {
b.Errorf( " %s", err)
}
}
}
}
}
b. Run( "basic", func(b *testing.B) {
props := [] interface{}{
&struct {
Nested struct {
S string
}
}{},
}
bp := `
m {
nested: {
s: "abc",
},
}
`
run(b, props, bp)
})
b. Run( "interface", func(b *testing.B) {
props := [] interface{}{
&struct {
Nested interface{}
}{
Nested: ( *struct {
S string
})(nil),
},
}
bp := `
m {
nested: {
s: "abc",
},
}
`
run(b, props, bp)
})
b. Run( "many", func(b *testing.B) {
props := [] interface{}{
&struct {
A *string
B *string
C *string
D *string
E *string
F *string
G *string
H *string
I *string
J *string
}{},
}
bp := `
m {
a: "a",
b: "b",
c: "c",
d: "d",
e: "e",
f: "f",
g: "g",
h: "h",
i: "i",
j: "j",
}
`
run(b, props, bp)
})
b. Run( "deep", func(b *testing.B) {
props := [] interface{}{
&struct {
Nested struct {
Nested struct {
Nested struct {
Nested struct {
Nested struct {
Nested struct {
Nested struct {
Nested struct {
Nested struct {
Nested struct {
S string
}
}
}
}
}
}
}
}
}
}
}{},
}
bp := `
m {
nested: { nested: { nested: { nested: { nested: {
nested: { nested: { nested: { nested: { nested: {
s: "abc",
}, }, }, }, },
}, }, }, }, },
}
`
run(b, props, bp)
})
b. Run( "mix", func(b *testing.B) {
props := [] interface{}{
&struct {
Name string
Flag bool
Settings [] string
Perarch *struct {
Arm string
Arm64 string
}
Configvars []struct {
Name string
Values [] string
}
}{},
}
bp := `
m {
name: "mymodule",
flag: true,
settings: [ "foo1", "foo2", "foo3",],
perarch: {
arm: "32",
arm64: "64",
},
configvars: [
{ name: "var1", values: [ "var1:1", "var1:2", ], },
{ name: "var2", values: [ "var2:1", "var2:2", ], },
],
}
`
run(b, props, bp)
})
}
func TestRemoveUnnecessaryUnusedNames(t *testing.T) {
testCases := []struct {
name string
input [] string
output [] string
}{
{
name: "no unused names",
input: [] string{},
output: [] string{},
},
{
name: "only one unused name",
input: [] string{ "a.b.c"},
output: [] string{ "a.b.c"},
},
{
name: "unused names in a chain",
input: [] string{ "a", "a.b", "a.b.c"},
output: [] string{ "a.b.c"},
},
{
name: "unused names unrelated",
input: [] string{ "a.b.c", "s.t", "x.y"},
output: [] string{ "a.b.c", "s.t", "x.y"},
},
{
name: "unused names partially related one",
input: [] string{ "a.b", "a.b.c", "a.b.d"},
output: [] string{ "a.b.c", "a.b.d"},
},
{
name: "unused names partially related two",
input: [] string{ "a", "a.b.c", "a.c"},
output: [] string{ "a.b.c", "a.c"},
},
{
name: "unused names partially related three",
input: [] string{ "a.b.c", "b.c", "c"},
output: [] string{ "a.b.c", "b.c", "c"},
},
}
for _, testCase := range testCases {
t. Run(testCase.name, func(t *testing.T) {
simplifiedNames := removeUnnecessaryUnusedNames(testCase. input)
if !reflect.DeepEqual(simplifiedNames, testCase.output) {
t.Errorf( "test case: %s", testCase.name)
t.Errorf( " input: %s", testCase. input)
t.Errorf( " expect: %s", testCase.output)
t.Errorf( " got: %s", simplifiedNames)
}
})
}
}
[Dauer der Verarbeitung: 0.11 Sekunden, vorverarbeitet 2026-10-24]
|
2026-11-01
|