route/vendor/github.com/mitchellh/copystructure/copystructure_test.go

1084 lines
17 KiB
Go

package copystructure
import (
"fmt"
"reflect"
"sync"
"testing"
"time"
)
func TestCopy_complex(t *testing.T) {
v := map[string]interface{}{
"foo": []string{"a", "b"},
"bar": "baz",
}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}
func TestCopy_interfacePointer(t *testing.T) {
type Nested struct {
Field string
}
type Test struct {
Value *interface{}
}
ifacePtr := func(v interface{}) *interface{} {
return &v
}
v := Test{
Value: ifacePtr(Nested{Field: "111"}),
}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}
func TestCopy_primitive(t *testing.T) {
cases := []interface{}{
42,
"foo",
1.2,
}
for _, tc := range cases {
result, err := Copy(tc)
if err != nil {
t.Fatalf("err: %s", err)
}
if result != tc {
t.Fatalf("bad: %#v", result)
}
}
}
func TestCopy_primitivePtr(t *testing.T) {
i := 42
s := "foo"
f := 1.2
cases := []interface{}{
&i,
&s,
&f,
}
for i, tc := range cases {
result, err := Copy(tc)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, tc) {
t.Fatalf("%d exptected: %#v\nbad: %#v", i, tc, result)
}
}
}
func TestCopy_map(t *testing.T) {
v := map[string]interface{}{
"bar": "baz",
}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}
func TestCopy_array(t *testing.T) {
v := [2]string{"bar", "baz"}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}
func TestCopy_pointerToArray(t *testing.T) {
v := &[2]string{"bar", "baz"}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}
func TestCopy_slice(t *testing.T) {
v := []string{"bar", "baz"}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}
func TestCopy_pointerToSlice(t *testing.T) {
v := &[]string{"bar", "baz"}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}
func TestCopy_pointerToMap(t *testing.T) {
v := &map[string]string{"bar": "baz"}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}
func TestCopy_struct(t *testing.T) {
type test struct {
Value string
}
v := test{Value: "foo"}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}
func TestCopy_structPtr(t *testing.T) {
type test struct {
Value string
}
v := &test{Value: "foo"}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}
func TestCopy_structNil(t *testing.T) {
type test struct {
Value string
}
var v *test
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
if v, ok := result.(*test); !ok {
t.Fatalf("bad: %#v", result)
} else if v != nil {
t.Fatalf("bad: %#v", v)
}
}
func TestCopy_structNested(t *testing.T) {
type TestInner struct{}
type Test struct {
Test *TestInner
}
v := Test{}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}
func TestCopy_structWithNestedArray(t *testing.T) {
type TestInner struct {
Value string
}
type Test struct {
Value [2]TestInner
}
v := Test{
Value: [2]TestInner{
{Value: "bar"},
{Value: "baz"},
},
}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}
func TestCopy_structWithPointerToSliceField(t *testing.T) {
type Test struct {
Value *[]string
}
v := Test{
Value: &[]string{"bar", "baz"},
}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}
func TestCopy_structWithPointerToArrayField(t *testing.T) {
type Test struct {
Value *[2]string
}
v := Test{
Value: &[2]string{"bar", "baz"},
}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}
func TestCopy_structWithPointerToMapField(t *testing.T) {
type Test struct {
Value *map[string]string
}
v := Test{
Value: &map[string]string{"bar": "baz"},
}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}
func TestCopy_structUnexported(t *testing.T) {
type test struct {
Value string
private string
}
v := test{Value: "foo"}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}
func TestCopy_structUnexportedMap(t *testing.T) {
type Sub struct {
Foo map[string]interface{}
}
type test struct {
Value string
private Sub
}
v := test{
Value: "foo",
private: Sub{
Foo: map[string]interface{}{
"yo": 42,
},
},
}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
// private should not be copied
v.private = Sub{}
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad:\n\n%#v\n\n%#v", result, v)
}
}
func TestCopy_structUnexportedArray(t *testing.T) {
type Sub struct {
Foo [2]string
}
type test struct {
Value string
private Sub
}
v := test{
Value: "foo",
private: Sub{
Foo: [2]string{"bar", "baz"},
},
}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
// private should not be copied
v.private = Sub{}
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad:\n\n%#v\n\n%#v", result, v)
}
}
// This is testing an unexported field containing a slice of pointers, which
// was a crashing case found in Terraform.
func TestCopy_structUnexportedPtrMap(t *testing.T) {
type Foo interface{}
type Sub struct {
List []Foo
}
type test struct {
Value string
private *Sub
}
v := test{
Value: "foo",
private: &Sub{
List: []Foo{&Sub{}},
},
}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
// private should not be copied
v.private = nil
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad:\n\n%#v\n\n%#v", result, v)
}
}
func TestCopy_nestedStructUnexported(t *testing.T) {
type subTest struct {
mine string
}
type test struct {
Value string
private subTest
}
v := test{Value: "foo"}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}
func TestCopy_time(t *testing.T) {
type test struct {
Value time.Time
}
v := test{Value: time.Now().UTC()}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}
func TestCopy_aliased(t *testing.T) {
type (
Int int
Str string
Map map[Int]interface{}
Slice []Str
)
v := Map{
1: Map{10: 20},
2: Map(nil),
3: Slice{"a", "b"},
}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}
type EmbeddedLocker struct {
sync.Mutex
Map map[int]int
}
func TestCopy_embeddedLocker(t *testing.T) {
v := &EmbeddedLocker{
Map: map[int]int{42: 111},
}
// start locked to prevent copying
v.Lock()
var result interface{}
var err error
copied := make(chan bool)
go func() {
result, err = Config{Lock: true}.Copy(v)
close(copied)
}()
// pause slightly to make sure copying is blocked
select {
case <-copied:
t.Fatal("copy completed while locked!")
case <-time.After(100 * time.Millisecond):
v.Unlock()
}
<-copied
// test that the mutex is in the correct state
result.(*EmbeddedLocker).Lock()
result.(*EmbeddedLocker).Unlock()
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}
// this will trigger the race detector, and usually panic if the original
// struct isn't properly locked during Copy
func TestCopy_lockRace(t *testing.T) {
v := &EmbeddedLocker{
Map: map[int]int{},
}
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for i := 0; i < 100; i++ {
v.Lock()
v.Map[i] = i
v.Unlock()
}
}()
wg.Add(1)
go func() {
defer wg.Done()
Config{Lock: true}.Copy(v)
}()
}
wg.Wait()
result, err := Config{Lock: true}.Copy(v)
// test that the mutex is in the correct state
result.(*EmbeddedLocker).Lock()
result.(*EmbeddedLocker).Unlock()
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}
type LockedField struct {
String string
Locker *EmbeddedLocker
// this should not get locked or have its state copied
Mutex sync.Mutex
nilMutex *sync.Mutex
}
func TestCopy_lockedField(t *testing.T) {
v := &LockedField{
String: "orig",
Locker: &EmbeddedLocker{
Map: map[int]int{42: 111},
},
}
// start locked to prevent copying
v.Locker.Lock()
v.Mutex.Lock()
var result interface{}
var err error
copied := make(chan bool)
go func() {
result, err = Config{Lock: true}.Copy(v)
close(copied)
}()
// pause slightly to make sure copying is blocked
select {
case <-copied:
t.Fatal("copy completed while locked!")
case <-time.After(100 * time.Millisecond):
v.Locker.Unlock()
}
<-copied
// test that the mutexes are in the correct state
result.(*LockedField).Locker.Lock()
result.(*LockedField).Locker.Unlock()
result.(*LockedField).Mutex.Lock()
result.(*LockedField).Mutex.Unlock()
// this wasn't blocking, but should be unlocked for DeepEqual
v.Mutex.Unlock()
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("expected:\n%#v\nbad:\n%#v\n", v, result)
}
}
// test something that doesn't contain a lock internally
type lockedMap map[int]int
var mapLock sync.Mutex
func (m lockedMap) Lock() { mapLock.Lock() }
func (m lockedMap) Unlock() { mapLock.Unlock() }
func TestCopy_lockedMap(t *testing.T) {
v := lockedMap{1: 2}
v.Lock()
var result interface{}
var err error
copied := make(chan bool)
go func() {
result, err = Config{Lock: true}.Copy(&v)
close(copied)
}()
// pause slightly to make sure copying is blocked
select {
case <-copied:
t.Fatal("copy completed while locked!")
case <-time.After(100 * time.Millisecond):
v.Unlock()
}
<-copied
// test that the mutex is in the correct state
result.(*lockedMap).Lock()
result.(*lockedMap).Unlock()
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, &v) {
t.Fatalf("bad: %#v", result)
}
}
// Use an RLock if available
type RLocker struct {
sync.RWMutex
Map map[int]int
}
func TestCopy_rLocker(t *testing.T) {
v := &RLocker{
Map: map[int]int{1: 2},
}
v.Lock()
var result interface{}
var err error
copied := make(chan bool)
go func() {
result, err = Config{Lock: true}.Copy(v)
close(copied)
}()
// pause slightly to make sure copying is blocked
select {
case <-copied:
t.Fatal("copy completed while locked!")
case <-time.After(100 * time.Millisecond):
v.Unlock()
}
<-copied
// test that the mutex is in the correct state
vCopy := result.(*RLocker)
vCopy.Lock()
vCopy.Unlock()
vCopy.RLock()
vCopy.RUnlock()
// now make sure we can copy during an RLock
v.RLock()
result, err = Config{Lock: true}.Copy(v)
if err != nil {
t.Fatal(err)
}
v.RUnlock()
vCopy = result.(*RLocker)
vCopy.Lock()
vCopy.Unlock()
vCopy.RLock()
vCopy.RUnlock()
if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}
// Test that we don't panic when encountering nil Lockers
func TestCopy_missingLockedField(t *testing.T) {
v := &LockedField{
String: "orig",
}
result, err := Config{Lock: true}.Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("expected:\n%#v\nbad:\n%#v\n", v, result)
}
}
type PointerLocker struct {
Mu sync.Mutex
}
func (p *PointerLocker) Lock() { p.Mu.Lock() }
func (p *PointerLocker) Unlock() { p.Mu.Unlock() }
func TestCopy_pointerLockerNil(t *testing.T) {
v := struct {
P *PointerLocker
}{}
_, err := Config{Lock: true}.Copy(&v)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestCopy_sliceWithNil(t *testing.T) {
v := [](*int){nil}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("expected:\n%#v\ngot:\n%#v", v, result)
}
}
func TestCopy_mapWithNil(t *testing.T) {
v := map[int](*int){0: nil}
result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(result, v) {
t.Fatalf("expected:\n%#v\ngot:\n%#v", v, result)
}
}
// While this is safe to lock and copy directly, copystructure requires a
// pointer to reflect the value safely.
func TestCopy_valueWithLockPointer(t *testing.T) {
v := struct {
*sync.Mutex
X int
}{
Mutex: &sync.Mutex{},
X: 3,
}
_, err := Config{Lock: true}.Copy(v)
if err != errPointerRequired {
t.Fatalf("expected errPointerRequired, got: %v", err)
}
}
func TestCopy_mapWithPointers(t *testing.T) {
type T struct {
S string
}
v := map[string]interface{}{
"a": &T{S: "hello"},
}
result, err := Copy(v)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(v, result) {
t.Fatalf("%#v", result)
}
}
func TestCopy_structWithMapWithPointers(t *testing.T) {
type T struct {
S string
M map[string]interface{}
}
v := &T{
S: "a",
M: map[string]interface{}{
"b": &T{
S: "b",
},
},
}
result, err := Copy(v)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(v, result) {
t.Fatal(result)
}
}
type testT struct {
N int
Spp **string
X testX
Xp *testX
Xpp **testX
}
type testX struct {
Tp *testT
Tpp **testT
Ip *interface{}
Ep *error
S fmt.Stringer
}
type stringer struct{}
func (s *stringer) String() string {
return "test string"
}
func TestCopy_structWithPointersAndInterfaces(t *testing.T) {
// test that we can copy various nested and chained pointers and interfaces
s := "val"
sp := &s
spp := &sp
i := interface{}(11)
tp := &testT{
N: 2,
}
xp := &testX{
Tp: tp,
Tpp: &tp,
Ip: &i,
S: &stringer{},
}
v := &testT{
N: 1,
Spp: spp,
X: testX{},
Xp: xp,
Xpp: &xp,
}
result, err := Copy(v)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(v, result) {
t.Fatal(result)
}
}
func Test_pointerInterfacePointer(t *testing.T) {
s := "hi"
si := interface{}(&s)
sip := &si
result, err := Copy(sip)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(sip, result) {
t.Fatalf("%#v != %#v\n", sip, result)
}
}
func Test_pointerInterfacePointer2(t *testing.T) {
type T struct {
I *interface{}
J **fmt.Stringer
}
x := 1
y := &stringer{}
i := interface{}(&x)
j := fmt.Stringer(y)
jp := &j
v := &T{
I: &i,
J: &jp,
}
result, err := Copy(v)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(v, result) {
t.Fatalf("%#v != %#v\n", v, result)
}
}
// This test catches a bug that happened when unexported fields were
// first their subsequent fields wouldn't be copied.
func TestCopy_unexportedFieldFirst(t *testing.T) {
type P struct {
mu sync.Mutex
Old, New string
}
type T struct {
M map[string]*P
}
v := &T{
M: map[string]*P{
"a": &P{Old: "", New: "2"},
},
}
result, err := Copy(v)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(v, result) {
t.Fatalf("\n%#v\n\n%#v", v, result)
}
}
func TestCopy_nilPointerInSlice(t *testing.T) {
type T struct {
Ps []*int
}
v := &T{
Ps: []*int{nil},
}
result, err := Copy(v)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(v, result) {
t.Fatalf("\n%#v\n\n%#v", v, result)
}
}
//-------------------------------------------------------------------
// The tests below all tests various pointer cases around copying
// a structure that uses a defined Copier. This was originally raised
// around issue #26.
func TestCopy_timePointer(t *testing.T) {
type T struct {
Value *time.Time
}
now := time.Now()
v := &T{
Value: &now,
}
result, err := Copy(v)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(v, result) {
t.Fatalf("\n%#v\n\n%#v", v, result)
}
}
func TestCopy_timeNonPointer(t *testing.T) {
type T struct {
Value time.Time
}
v := &T{
Value: time.Now(),
}
result, err := Copy(v)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(v, result) {
t.Fatalf("\n%#v\n\n%#v", v, result)
}
}
func TestCopy_timeDoublePointer(t *testing.T) {
type T struct {
Value **time.Time
}
now := time.Now()
nowP := &now
nowPP := &nowP
v := &T{
Value: nowPP,
}
result, err := Copy(v)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(v, result) {
t.Fatalf("\n%#v\n\n%#v", v, result)
}
}