123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847 |
- /*
- Copyright 2014 The Kubernetes Authors.
- 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 conversion
- import (
- "encoding/json"
- "fmt"
- "reflect"
- "strconv"
- "strings"
- "testing"
- "github.com/google/gofuzz"
- flag "github.com/spf13/pflag"
- "k8s.io/kubernetes/pkg/util/diff"
- )
- var fuzzIters = flag.Int("fuzz-iters", 50, "How many fuzzing iterations to do.")
- // Test a weird version/kind embedding format.
- type MyWeirdCustomEmbeddedVersionKindField struct {
- ID string `json:"ID,omitempty"`
- APIVersion string `json:"myVersionKey,omitempty"`
- ObjectKind string `json:"myKindKey,omitempty"`
- Z string `json:"Z,omitempty"`
- Y uint64 `json:"Y,omitempty"`
- }
- type TestType1 struct {
- MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
- A string `json:"A,omitempty"`
- B int `json:"B,omitempty"`
- C int8 `json:"C,omitempty"`
- D int16 `json:"D,omitempty"`
- E int32 `json:"E,omitempty"`
- F int64 `json:"F,omitempty"`
- G uint `json:"G,omitempty"`
- H uint8 `json:"H,omitempty"`
- I uint16 `json:"I,omitempty"`
- J uint32 `json:"J,omitempty"`
- K uint64 `json:"K,omitempty"`
- L bool `json:"L,omitempty"`
- M map[string]int `json:"M,omitempty"`
- N map[string]TestType2 `json:"N,omitempty"`
- O *TestType2 `json:"O,omitempty"`
- P []TestType2 `json:"Q,omitempty"`
- }
- type TestType2 struct {
- A string `json:"A,omitempty"`
- B int `json:"B,omitempty"`
- }
- type ExternalTestType2 struct {
- A string `json:"A,omitempty"`
- B int `json:"B,omitempty"`
- }
- type ExternalTestType1 struct {
- MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
- A string `json:"A,omitempty"`
- B int `json:"B,omitempty"`
- C int8 `json:"C,omitempty"`
- D int16 `json:"D,omitempty"`
- E int32 `json:"E,omitempty"`
- F int64 `json:"F,omitempty"`
- G uint `json:"G,omitempty"`
- H uint8 `json:"H,omitempty"`
- I uint16 `json:"I,omitempty"`
- J uint32 `json:"J,omitempty"`
- K uint64 `json:"K,omitempty"`
- L bool `json:"L,omitempty"`
- M map[string]int `json:"M,omitempty"`
- N map[string]ExternalTestType2 `json:"N,omitempty"`
- O *ExternalTestType2 `json:"O,omitempty"`
- P []ExternalTestType2 `json:"Q,omitempty"`
- }
- func testLogger(t *testing.T) DebugLogger {
- // We don't set logger to eliminate rubbish logs in tests.
- // If you want to switch it, simply switch it to: "return t"
- return nil
- }
- func TestConverter_byteSlice(t *testing.T) {
- c := NewConverter(DefaultNameFunc)
- src := []byte{1, 2, 3}
- dest := []byte{}
- err := c.Convert(&src, &dest, 0, nil)
- if err != nil {
- t.Fatalf("expected no error")
- }
- if e, a := src, dest; !reflect.DeepEqual(e, a) {
- t.Errorf("expected %#v, got %#v", e, a)
- }
- }
- func TestConverter_MismatchedTypes(t *testing.T) {
- c := NewConverter(DefaultNameFunc)
- err := c.RegisterConversionFunc(
- func(in *[]string, out *int, s Scope) error {
- if str, err := strconv.Atoi((*in)[0]); err != nil {
- return err
- } else {
- *out = str
- return nil
- }
- },
- )
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
- src := []string{"5"}
- var dest *int
- err = c.Convert(&src, &dest, 0, nil)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if e, a := 5, *dest; e != a {
- t.Errorf("expected %#v, got %#v", e, a)
- }
- }
- func TestConverter_DefaultConvert(t *testing.T) {
- type A struct {
- Foo string
- Baz int
- }
- type B struct {
- Bar string
- Baz int
- }
- c := NewConverter(DefaultNameFunc)
- c.Debug = testLogger(t)
- c.nameFunc = func(t reflect.Type) string { return "MyType" }
- // Ensure conversion funcs can call DefaultConvert to get default behavior,
- // then fixup remaining fields manually
- err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
- if err := s.DefaultConvert(in, out, IgnoreMissingFields); err != nil {
- return err
- }
- out.Bar = in.Foo
- return nil
- })
- if err != nil {
- t.Fatalf("unexpected error %v", err)
- }
- x := A{"hello, intrepid test reader!", 3}
- y := B{}
- err = c.Convert(&x, &y, 0, nil)
- if err != nil {
- t.Fatalf("unexpected error %v", err)
- }
- if e, a := x.Foo, y.Bar; e != a {
- t.Errorf("expected %v, got %v", e, a)
- }
- if e, a := x.Baz, y.Baz; e != a {
- t.Errorf("expected %v, got %v", e, a)
- }
- }
- func TestConverter_DeepCopy(t *testing.T) {
- type A struct {
- Foo *string
- Bar []string
- Baz interface{}
- Qux map[string]string
- }
- c := NewConverter(DefaultNameFunc)
- c.Debug = testLogger(t)
- foo, baz := "foo", "baz"
- x := A{
- Foo: &foo,
- Bar: []string{"bar"},
- Baz: &baz,
- Qux: map[string]string{"qux": "qux"},
- }
- y := A{}
- if err := c.Convert(&x, &y, 0, nil); err != nil {
- t.Fatalf("unexpected error %v", err)
- }
- *x.Foo = "foo2"
- x.Bar[0] = "bar2"
- *x.Baz.(*string) = "baz2"
- x.Qux["qux"] = "qux2"
- if e, a := *x.Foo, *y.Foo; e == a {
- t.Errorf("expected difference between %v and %v", e, a)
- }
- if e, a := x.Bar, y.Bar; reflect.DeepEqual(e, a) {
- t.Errorf("expected difference between %v and %v", e, a)
- }
- if e, a := *x.Baz.(*string), *y.Baz.(*string); e == a {
- t.Errorf("expected difference between %v and %v", e, a)
- }
- if e, a := x.Qux, y.Qux; reflect.DeepEqual(e, a) {
- t.Errorf("expected difference between %v and %v", e, a)
- }
- }
- func TestConverter_CallsRegisteredFunctions(t *testing.T) {
- type A struct {
- Foo string
- Baz int
- }
- type B struct {
- Bar string
- Baz int
- }
- type C struct{}
- c := NewConverter(DefaultNameFunc)
- c.Debug = testLogger(t)
- err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
- out.Bar = in.Foo
- return s.Convert(&in.Baz, &out.Baz, 0)
- })
- if err != nil {
- t.Fatalf("unexpected error %v", err)
- }
- err = c.RegisterConversionFunc(func(in *B, out *A, s Scope) error {
- out.Foo = in.Bar
- return s.Convert(&in.Baz, &out.Baz, 0)
- })
- if err != nil {
- t.Fatalf("unexpected error %v", err)
- }
- x := A{"hello, intrepid test reader!", 3}
- y := B{}
- err = c.Convert(&x, &y, 0, nil)
- if err != nil {
- t.Fatalf("unexpected error %v", err)
- }
- if e, a := x.Foo, y.Bar; e != a {
- t.Errorf("expected %v, got %v", e, a)
- }
- if e, a := x.Baz, y.Baz; e != a {
- t.Errorf("expected %v, got %v", e, a)
- }
- z := B{"all your test are belong to us", 42}
- w := A{}
- err = c.Convert(&z, &w, 0, nil)
- if err != nil {
- t.Fatalf("unexpected error %v", err)
- }
- if e, a := z.Bar, w.Foo; e != a {
- t.Errorf("expected %v, got %v", e, a)
- }
- if e, a := z.Baz, w.Baz; e != a {
- t.Errorf("expected %v, got %v", e, a)
- }
- err = c.RegisterConversionFunc(func(in *A, out *C, s Scope) error {
- return fmt.Errorf("C can't store an A, silly")
- })
- if err != nil {
- t.Fatalf("unexpected error %v", err)
- }
- err = c.Convert(&A{}, &C{}, 0, nil)
- if err == nil {
- t.Errorf("unexpected non-error")
- }
- }
- func TestConverter_IgnoredConversion(t *testing.T) {
- type A struct{}
- type B struct{}
- count := 0
- c := NewConverter(DefaultNameFunc)
- if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
- count++
- return nil
- }); err != nil {
- t.Fatalf("unexpected error %v", err)
- }
- if err := c.RegisterIgnoredConversion(&A{}, &B{}); err != nil {
- t.Fatal(err)
- }
- a := A{}
- b := B{}
- if err := c.Convert(&a, &b, 0, nil); err != nil {
- t.Errorf("%v", err)
- }
- if count != 0 {
- t.Errorf("unexpected number of conversion invocations")
- }
- }
- func TestConverter_IgnoredConversionNested(t *testing.T) {
- type C string
- type A struct {
- C C
- }
- type B struct {
- C C
- }
- c := NewConverter(DefaultNameFunc)
- typed := C("")
- if err := c.RegisterIgnoredConversion(&typed, &typed); err != nil {
- t.Fatal(err)
- }
- a := A{C: C("test")}
- b := B{C: C("other")}
- if err := c.Convert(&a, &b, AllowDifferentFieldTypeNames, nil); err != nil {
- t.Errorf("%v", err)
- }
- if b.C != C("other") {
- t.Errorf("expected no conversion of field C: %#v", b)
- }
- }
- func TestConverter_GeneratedConversionOverriden(t *testing.T) {
- type A struct{}
- type B struct{}
- c := NewConverter(DefaultNameFunc)
- if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
- return nil
- }); err != nil {
- t.Fatalf("unexpected error %v", err)
- }
- if err := c.RegisterGeneratedConversionFunc(func(in *A, out *B, s Scope) error {
- return fmt.Errorf("generated function should be overriden")
- }); err != nil {
- t.Fatalf("unexpected error %v", err)
- }
- a := A{}
- b := B{}
- if err := c.Convert(&a, &b, 0, nil); err != nil {
- t.Errorf("%v", err)
- }
- }
- func TestConverter_WithConversionOverriden(t *testing.T) {
- type A struct{}
- type B struct{}
- c := NewConverter(DefaultNameFunc)
- if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
- return fmt.Errorf("conversion function should be overriden")
- }); err != nil {
- t.Fatalf("unexpected error %v", err)
- }
- if err := c.RegisterGeneratedConversionFunc(func(in *A, out *B, s Scope) error {
- return fmt.Errorf("generated function should be overriden")
- }); err != nil {
- t.Fatalf("unexpected error %v", err)
- }
- ext := NewConversionFuncs()
- ext.Add(func(in *A, out *B, s Scope) error {
- return nil
- })
- newc := c.WithConversions(ext)
- a := A{}
- b := B{}
- if err := c.Convert(&a, &b, 0, nil); err == nil || err.Error() != "conversion function should be overriden" {
- t.Errorf("unexpected error: %v", err)
- }
- if err := newc.Convert(&a, &b, 0, nil); err != nil {
- t.Errorf("%v", err)
- }
- }
- func TestConverter_MapsStringArrays(t *testing.T) {
- type A struct {
- Foo string
- Baz int
- Other string
- }
- c := NewConverter(DefaultNameFunc)
- c.Debug = testLogger(t)
- if err := c.RegisterConversionFunc(func(input *[]string, out *string, s Scope) error {
- if len(*input) == 0 {
- *out = ""
- }
- *out = (*input)[0]
- return nil
- }); err != nil {
- t.Fatalf("unexpected error %v", err)
- }
- x := map[string][]string{
- "Foo": {"bar"},
- "Baz": {"1"},
- "Other": {"", "test"},
- "other": {"wrong"},
- }
- y := A{"test", 2, "something"}
- if err := c.Convert(&x, &y, AllowDifferentFieldTypeNames, nil); err == nil {
- t.Error("unexpected non-error")
- }
- if err := c.RegisterConversionFunc(func(input *[]string, out *int, s Scope) error {
- if len(*input) == 0 {
- *out = 0
- }
- str := (*input)[0]
- i, err := strconv.Atoi(str)
- if err != nil {
- return err
- }
- *out = i
- return nil
- }); err != nil {
- t.Fatalf("unexpected error %v", err)
- }
- if err := c.Convert(&x, &y, AllowDifferentFieldTypeNames, nil); err != nil {
- t.Fatalf("unexpected error %v", err)
- }
- if !reflect.DeepEqual(y, A{"bar", 1, ""}) {
- t.Errorf("unexpected result: %#v", y)
- }
- }
- func TestConverter_MapsStringArraysWithMappingKey(t *testing.T) {
- type A struct {
- Foo string `json:"test"`
- Baz int
- Other string
- }
- c := NewConverter(DefaultNameFunc)
- c.Debug = testLogger(t)
- if err := c.RegisterConversionFunc(func(input *[]string, out *string, s Scope) error {
- if len(*input) == 0 {
- *out = ""
- }
- *out = (*input)[0]
- return nil
- }); err != nil {
- t.Fatalf("unexpected error %v", err)
- }
- x := map[string][]string{
- "Foo": {"bar"},
- "test": {"baz"},
- }
- y := A{"", 0, ""}
- if err := c.Convert(&x, &y, AllowDifferentFieldTypeNames|IgnoreMissingFields, &Meta{}); err != nil {
- t.Fatalf("unexpected error %v", err)
- }
- if !reflect.DeepEqual(y, A{"bar", 0, ""}) {
- t.Errorf("unexpected result: %#v", y)
- }
- mapping := func(key string, sourceTag, destTag reflect.StructTag) (source string, dest string) {
- if s := destTag.Get("json"); len(s) > 0 {
- return strings.SplitN(s, ",", 2)[0], key
- }
- return key, key
- }
- if err := c.Convert(&x, &y, AllowDifferentFieldTypeNames|IgnoreMissingFields, &Meta{KeyNameMapping: mapping}); err != nil {
- t.Fatalf("unexpected error %v", err)
- }
- if !reflect.DeepEqual(y, A{"baz", 0, ""}) {
- t.Errorf("unexpected result: %#v", y)
- }
- }
- func TestConverter_fuzz(t *testing.T) {
- // Use the same types from the scheme test.
- table := []struct {
- from, to, check interface{}
- }{
- {&TestType1{}, &ExternalTestType1{}, &TestType1{}},
- {&ExternalTestType1{}, &TestType1{}, &ExternalTestType1{}},
- }
- f := fuzz.New().NilChance(.5).NumElements(0, 100)
- c := NewConverter(DefaultNameFunc)
- c.nameFunc = func(t reflect.Type) string {
- // Hide the fact that we don't have separate packages for these things.
- return map[reflect.Type]string{
- reflect.TypeOf(TestType1{}): "TestType1",
- reflect.TypeOf(ExternalTestType1{}): "TestType1",
- reflect.TypeOf(TestType2{}): "TestType2",
- reflect.TypeOf(ExternalTestType2{}): "TestType2",
- }[t]
- }
- c.Debug = testLogger(t)
- for i, item := range table {
- for j := 0; j < *fuzzIters; j++ {
- f.Fuzz(item.from)
- err := c.Convert(item.from, item.to, 0, nil)
- if err != nil {
- t.Errorf("(%v, %v): unexpected error: %v", i, j, err)
- continue
- }
- err = c.Convert(item.to, item.check, 0, nil)
- if err != nil {
- t.Errorf("(%v, %v): unexpected error: %v", i, j, err)
- continue
- }
- if e, a := item.from, item.check; !reflect.DeepEqual(e, a) {
- t.Errorf("(%v, %v): unexpected diff: %v", i, j, objDiff(e, a))
- }
- }
- }
- }
- func TestConverter_MapElemAddr(t *testing.T) {
- type Foo struct {
- A map[int]int
- }
- type Bar struct {
- A map[string]string
- }
- c := NewConverter(DefaultNameFunc)
- c.Debug = testLogger(t)
- err := c.RegisterConversionFunc(
- func(in *int, out *string, s Scope) error {
- *out = fmt.Sprintf("%v", *in)
- return nil
- },
- )
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
- err = c.RegisterConversionFunc(
- func(in *string, out *int, s Scope) error {
- if str, err := strconv.Atoi(*in); err != nil {
- return err
- } else {
- *out = str
- return nil
- }
- },
- )
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
- f := fuzz.New().NilChance(0).NumElements(3, 3)
- first := Foo{}
- second := Bar{}
- f.Fuzz(&first)
- err = c.Convert(&first, &second, AllowDifferentFieldTypeNames, nil)
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
- third := Foo{}
- err = c.Convert(&second, &third, AllowDifferentFieldTypeNames, nil)
- if e, a := first, third; !reflect.DeepEqual(e, a) {
- t.Errorf("Unexpected diff: %v", objDiff(e, a))
- }
- }
- func TestConverter_tags(t *testing.T) {
- type Foo struct {
- A string `test:"foo"`
- }
- type Bar struct {
- A string `test:"bar"`
- }
- c := NewConverter(DefaultNameFunc)
- c.Debug = testLogger(t)
- err := c.RegisterConversionFunc(
- func(in *string, out *string, s Scope) error {
- if e, a := "foo", s.SrcTag().Get("test"); e != a {
- t.Errorf("expected %v, got %v", e, a)
- }
- if e, a := "bar", s.DestTag().Get("test"); e != a {
- t.Errorf("expected %v, got %v", e, a)
- }
- return nil
- },
- )
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
- err = c.Convert(&Foo{}, &Bar{}, AllowDifferentFieldTypeNames, nil)
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
- }
- func TestConverter_meta(t *testing.T) {
- type Foo struct{ A string }
- type Bar struct{ A string }
- c := NewConverter(DefaultNameFunc)
- c.Debug = testLogger(t)
- checks := 0
- err := c.RegisterConversionFunc(
- func(in *Foo, out *Bar, s Scope) error {
- if s.Meta() == nil {
- t.Errorf("Meta did not get passed!")
- }
- checks++
- s.Convert(&in.A, &out.A, 0)
- return nil
- },
- )
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
- err = c.RegisterConversionFunc(
- func(in *string, out *string, s Scope) error {
- if s.Meta() == nil {
- t.Errorf("Meta did not get passed a second time!")
- }
- checks++
- return nil
- },
- )
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
- err = c.Convert(&Foo{}, &Bar{}, 0, &Meta{})
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
- if checks != 2 {
- t.Errorf("Registered functions did not get called.")
- }
- }
- func TestConverter_flags(t *testing.T) {
- type Foo struct{ A string }
- type Bar struct{ A string }
- table := []struct {
- from, to interface{}
- flags FieldMatchingFlags
- shouldSucceed bool
- }{
- // Check that DestFromSource allows extra fields only in source.
- {
- from: &struct{ A string }{},
- to: &struct{ A, B string }{},
- flags: DestFromSource,
- shouldSucceed: false,
- }, {
- from: &struct{ A, B string }{},
- to: &struct{ A string }{},
- flags: DestFromSource,
- shouldSucceed: true,
- },
- // Check that SourceToDest allows for extra fields only in dest.
- {
- from: &struct{ A string }{},
- to: &struct{ A, B string }{},
- flags: SourceToDest,
- shouldSucceed: true,
- }, {
- from: &struct{ A, B string }{},
- to: &struct{ A string }{},
- flags: SourceToDest,
- shouldSucceed: false,
- },
- // Check that IgnoreMissingFields makes the above failure cases pass.
- {
- from: &struct{ A string }{},
- to: &struct{ A, B string }{},
- flags: DestFromSource | IgnoreMissingFields,
- shouldSucceed: true,
- }, {
- from: &struct{ A, B string }{},
- to: &struct{ A string }{},
- flags: SourceToDest | IgnoreMissingFields,
- shouldSucceed: true,
- },
- // Check that the field type name must match unless
- // AllowDifferentFieldTypeNames is specified.
- {
- from: &struct{ A, B Foo }{},
- to: &struct{ A Bar }{},
- flags: DestFromSource,
- shouldSucceed: false,
- }, {
- from: &struct{ A Foo }{},
- to: &struct{ A, B Bar }{},
- flags: SourceToDest,
- shouldSucceed: false,
- }, {
- from: &struct{ A, B Foo }{},
- to: &struct{ A Bar }{},
- flags: DestFromSource | AllowDifferentFieldTypeNames,
- shouldSucceed: true,
- }, {
- from: &struct{ A Foo }{},
- to: &struct{ A, B Bar }{},
- flags: SourceToDest | AllowDifferentFieldTypeNames,
- shouldSucceed: true,
- },
- }
- f := fuzz.New().NilChance(.5).NumElements(0, 100)
- c := NewConverter(DefaultNameFunc)
- c.Debug = testLogger(t)
- for i, item := range table {
- for j := 0; j < *fuzzIters; j++ {
- f.Fuzz(item.from)
- err := c.Convert(item.from, item.to, item.flags, nil)
- if item.shouldSucceed && err != nil {
- t.Errorf("(%v, %v): unexpected error: %v", i, j, err)
- continue
- }
- if !item.shouldSucceed && err == nil {
- t.Errorf("(%v, %v): unexpected non-error", i, j)
- continue
- }
- }
- }
- }
- func TestConverter_FieldRename(t *testing.T) {
- type WeirdMeta struct {
- Name string
- Type string
- }
- type NameMeta struct {
- Name string
- }
- type TypeMeta struct {
- Type string
- }
- type A struct {
- WeirdMeta
- }
- type B struct {
- TypeMeta
- NameMeta
- }
- c := NewConverter(DefaultNameFunc)
- err := c.SetStructFieldCopy(WeirdMeta{}, "WeirdMeta", TypeMeta{}, "TypeMeta")
- if err != nil {
- t.Fatalf("unexpected error %v", err)
- }
- err = c.SetStructFieldCopy(WeirdMeta{}, "WeirdMeta", NameMeta{}, "NameMeta")
- if err != nil {
- t.Fatalf("unexpected error %v", err)
- }
- err = c.SetStructFieldCopy(TypeMeta{}, "TypeMeta", WeirdMeta{}, "WeirdMeta")
- if err != nil {
- t.Fatalf("unexpected error %v", err)
- }
- err = c.SetStructFieldCopy(NameMeta{}, "NameMeta", WeirdMeta{}, "WeirdMeta")
- if err != nil {
- t.Fatalf("unexpected error %v", err)
- }
- c.Debug = testLogger(t)
- aVal := &A{
- WeirdMeta: WeirdMeta{
- Name: "Foo",
- Type: "Bar",
- },
- }
- bVal := &B{
- TypeMeta: TypeMeta{"Bar"},
- NameMeta: NameMeta{"Foo"},
- }
- table := map[string]struct {
- from, to, expect interface{}
- flags FieldMatchingFlags
- }{
- "to": {
- aVal,
- &B{},
- bVal,
- AllowDifferentFieldTypeNames | SourceToDest | IgnoreMissingFields,
- },
- "from": {
- bVal,
- &A{},
- aVal,
- AllowDifferentFieldTypeNames | SourceToDest,
- },
- "toDestFirst": {
- aVal,
- &B{},
- bVal,
- AllowDifferentFieldTypeNames,
- },
- "fromDestFirst": {
- bVal,
- &A{},
- aVal,
- AllowDifferentFieldTypeNames | IgnoreMissingFields,
- },
- }
- for name, item := range table {
- err := c.Convert(item.from, item.to, item.flags, nil)
- if err != nil {
- t.Errorf("%v: unexpected error: %v", name, err)
- continue
- }
- if e, a := item.expect, item.to; !reflect.DeepEqual(e, a) {
- t.Errorf("%v: unexpected diff: %v", name, objDiff(e, a))
- }
- }
- }
- func objDiff(a, b interface{}) string {
- ab, err := json.Marshal(a)
- if err != nil {
- panic("a")
- }
- bb, err := json.Marshal(b)
- if err != nil {
- panic("b")
- }
- return diff.StringDiff(string(ab), string(bb))
- // An alternate diff attempt, in case json isn't showing you
- // the difference. (reflect.DeepEqual makes a distinction between
- // nil and empty slices, for example.)
- //return diff.StringDiff(
- // fmt.Sprintf("%#v", a),
- // fmt.Sprintf("%#v", b),
- //)
- }
|