mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-11-04 07:49:35 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			848 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			848 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2014 The Kubernetes Authors 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 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 || s.Meta().SrcVersion != "test" || s.Meta().DestVersion != "passes" {
 | 
						|
				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 || s.Meta().SrcVersion != "test" || s.Meta().DestVersion != "passes" {
 | 
						|
				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{SrcVersion: "test", DestVersion: "passes"})
 | 
						|
	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),
 | 
						|
	//)
 | 
						|
}
 |