Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  unpack_test.go   Sprache: unbekannt

 
Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs 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.29 Sekunden, vorverarbeitet 2026-06-28]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik