2017-05-18 18:54:23 +02:00
// Copyright 2013 Dario Castañé. All rights reserved.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package mergo
import (
2017-10-26 17:02:04 +02:00
"gopkg.in/yaml.v2"
2017-05-18 18:54:23 +02:00
"io/ioutil"
"reflect"
"testing"
"time"
)
type simpleTest struct {
Value int
}
type complexTest struct {
St simpleTest
sz int
ID string
}
2017-10-26 17:02:04 +02:00
type mapTest struct {
M map [ int ] int
}
type ifcTest struct {
I interface { }
}
2017-05-18 18:54:23 +02:00
type moreComplextText struct {
Ct complexTest
St simpleTest
Nt simpleTest
}
type pointerTest struct {
C * simpleTest
}
type sliceTest struct {
S [ ] int
}
func TestKb ( t * testing . T ) {
type testStruct struct {
Name string
KeyValue map [ string ] interface { }
}
akv := make ( map [ string ] interface { } )
akv [ "Key1" ] = "not value 1"
akv [ "Key2" ] = "value2"
a := testStruct { }
a . Name = "A"
a . KeyValue = akv
bkv := make ( map [ string ] interface { } )
bkv [ "Key1" ] = "value1"
bkv [ "Key3" ] = "value3"
b := testStruct { }
b . Name = "B"
b . KeyValue = bkv
ekv := make ( map [ string ] interface { } )
ekv [ "Key1" ] = "value1"
ekv [ "Key2" ] = "value2"
ekv [ "Key3" ] = "value3"
expected := testStruct { }
expected . Name = "B"
expected . KeyValue = ekv
Merge ( & b , a )
if ! reflect . DeepEqual ( b , expected ) {
t . Errorf ( "Actual: %#v did not match \nExpected: %#v" , b , expected )
}
}
func TestNil ( t * testing . T ) {
if err := Merge ( nil , nil ) ; err != ErrNilArguments {
t . Fail ( )
}
}
func TestDifferentTypes ( t * testing . T ) {
a := simpleTest { 42 }
b := 42
if err := Merge ( & a , b ) ; err != ErrDifferentArgumentsTypes {
t . Fail ( )
}
}
func TestSimpleStruct ( t * testing . T ) {
a := simpleTest { }
b := simpleTest { 42 }
if err := Merge ( & a , b ) ; err != nil {
t . FailNow ( )
}
if a . Value != 42 {
t . Fatalf ( "b not merged in properly: a.Value(%d) != b.Value(%d)" , a . Value , b . Value )
}
if ! reflect . DeepEqual ( a , b ) {
t . FailNow ( )
}
}
func TestComplexStruct ( t * testing . T ) {
a := complexTest { }
a . ID = "athing"
b := complexTest { simpleTest { 42 } , 1 , "bthing" }
if err := Merge ( & a , b ) ; err != nil {
t . FailNow ( )
}
if a . St . Value != 42 {
t . Fatalf ( "b not merged in properly: a.St.Value(%d) != b.St.Value(%d)" , a . St . Value , b . St . Value )
}
if a . sz == 1 {
t . Fatalf ( "a's private field sz not preserved from merge: a.sz(%d) == b.sz(%d)" , a . sz , b . sz )
}
if a . ID == b . ID {
t . Fatalf ( "a's field ID merged unexpectedly: a.ID(%s) == b.ID(%s)" , a . ID , b . ID )
}
}
func TestComplexStructWithOverwrite ( t * testing . T ) {
a := complexTest { simpleTest { 1 } , 1 , "do-not-overwrite-with-empty-value" }
b := complexTest { simpleTest { 42 } , 2 , "" }
expect := complexTest { simpleTest { 42 } , 1 , "do-not-overwrite-with-empty-value" }
if err := MergeWithOverwrite ( & a , b ) ; err != nil {
t . FailNow ( )
}
if ! reflect . DeepEqual ( a , expect ) {
t . Fatalf ( "Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n" , a , expect )
}
}
func TestPointerStruct ( t * testing . T ) {
s1 := simpleTest { }
s2 := simpleTest { 19 }
a := pointerTest { & s1 }
b := pointerTest { & s2 }
if err := Merge ( & a , b ) ; err != nil {
t . FailNow ( )
}
if a . C . Value != b . C . Value {
t . Fatalf ( "b not merged in properly: a.C.Value(%d) != b.C.Value(%d)" , a . C . Value , b . C . Value )
}
}
type embeddingStruct struct {
embeddedStruct
}
type embeddedStruct struct {
A string
}
func TestEmbeddedStruct ( t * testing . T ) {
tests := [ ] struct {
src embeddingStruct
dst embeddingStruct
expected embeddingStruct
} {
{
src : embeddingStruct {
embeddedStruct { "foo" } ,
} ,
dst : embeddingStruct {
embeddedStruct { "" } ,
} ,
expected : embeddingStruct {
embeddedStruct { "foo" } ,
} ,
} ,
{
src : embeddingStruct {
embeddedStruct { "" } ,
} ,
dst : embeddingStruct {
embeddedStruct { "bar" } ,
} ,
expected : embeddingStruct {
embeddedStruct { "bar" } ,
} ,
} ,
{
src : embeddingStruct {
embeddedStruct { "foo" } ,
} ,
dst : embeddingStruct {
embeddedStruct { "bar" } ,
} ,
expected : embeddingStruct {
embeddedStruct { "bar" } ,
} ,
} ,
}
for _ , test := range tests {
err := Merge ( & test . dst , test . src )
if err != nil {
t . Errorf ( "unexpected error: %v" , err )
continue
}
if ! reflect . DeepEqual ( test . dst , test . expected ) {
t . Errorf ( "unexpected output\nexpected:\n%+v\nsaw:\n%+v\n" , test . expected , test . dst )
}
}
}
func TestPointerStructNil ( t * testing . T ) {
a := pointerTest { nil }
b := pointerTest { & simpleTest { 19 } }
if err := Merge ( & a , b ) ; err != nil {
t . FailNow ( )
}
if a . C . Value != b . C . Value {
t . Fatalf ( "b not merged in a properly: a.C.Value(%d) != b.C.Value(%d)" , a . C . Value , b . C . Value )
}
}
func TestSliceStruct ( t * testing . T ) {
a := sliceTest { }
b := sliceTest { [ ] int { 1 , 2 , 3 } }
if err := Merge ( & a , b ) ; err != nil {
t . FailNow ( )
}
if len ( b . S ) != 3 {
t . FailNow ( )
}
if len ( a . S ) != len ( b . S ) {
t . Fatalf ( "b not merged in a proper way %d != %d" , len ( a . S ) , len ( b . S ) )
}
a = sliceTest { [ ] int { 1 } }
b = sliceTest { [ ] int { 1 , 2 , 3 } }
if err := Merge ( & a , b ) ; err != nil {
t . FailNow ( )
}
if len ( a . S ) != 1 {
t . FailNow ( )
}
if len ( a . S ) == len ( b . S ) {
t . Fatalf ( "b merged unexpectedly %d != %d" , len ( a . S ) , len ( b . S ) )
}
}
2017-10-26 17:02:04 +02:00
func TestEmptyMaps ( t * testing . T ) {
a := mapTest { }
b := mapTest {
map [ int ] int { } ,
}
if err := Merge ( & a , b ) ; err != nil {
t . Fail ( )
}
if ! reflect . DeepEqual ( a , b ) {
t . FailNow ( )
}
}
func TestEmptyToEmptyMaps ( t * testing . T ) {
a := mapTest { }
b := mapTest { }
if err := Merge ( & a , b ) ; err != nil {
t . Fail ( )
}
if ! reflect . DeepEqual ( a , b ) {
t . FailNow ( )
}
}
func TestEmptyToNotEmptyMaps ( t * testing . T ) {
a := mapTest { map [ int ] int {
1 : 2 ,
3 : 4 ,
} }
aa := mapTest { map [ int ] int {
1 : 2 ,
3 : 4 ,
} }
b := mapTest {
map [ int ] int { } ,
}
if err := Merge ( & a , b ) ; err != nil {
t . Fail ( )
}
if ! reflect . DeepEqual ( a , aa ) {
t . FailNow ( )
}
}
2017-05-18 18:54:23 +02:00
func TestMapsWithOverwrite ( t * testing . T ) {
m := map [ string ] simpleTest {
"a" : { } , // overwritten by 16
"b" : { 42 } , // not overwritten by empty value
"c" : { 13 } , // overwritten by 12
"d" : { 61 } ,
}
n := map [ string ] simpleTest {
"a" : { 16 } ,
"b" : { } ,
"c" : { 12 } ,
"e" : { 14 } ,
}
expect := map [ string ] simpleTest {
"a" : { 16 } ,
"b" : { } ,
"c" : { 12 } ,
"d" : { 61 } ,
"e" : { 14 } ,
}
if err := MergeWithOverwrite ( & m , n ) ; err != nil {
t . Fatalf ( err . Error ( ) )
}
if ! reflect . DeepEqual ( m , expect ) {
t . Fatalf ( "Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n" , m , expect )
}
}
func TestMaps ( t * testing . T ) {
m := map [ string ] simpleTest {
"a" : { } ,
"b" : { 42 } ,
"c" : { 13 } ,
"d" : { 61 } ,
}
n := map [ string ] simpleTest {
"a" : { 16 } ,
"b" : { } ,
"c" : { 12 } ,
"e" : { 14 } ,
}
expect := map [ string ] simpleTest {
"a" : { 0 } ,
"b" : { 42 } ,
"c" : { 13 } ,
"d" : { 61 } ,
"e" : { 14 } ,
}
if err := Merge ( & m , n ) ; err != nil {
t . Fatalf ( err . Error ( ) )
}
if ! reflect . DeepEqual ( m , expect ) {
t . Fatalf ( "Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n" , m , expect )
}
if m [ "a" ] . Value != 0 {
t . Fatalf ( ` n merged in m because I solved non-addressable map values TODO: m["a"].Value(%d) != n["a"].Value(%d) ` , m [ "a" ] . Value , n [ "a" ] . Value )
}
if m [ "b" ] . Value != 42 {
t . Fatalf ( ` n wrongly merged in m: m["b"].Value(%d) != n["b"].Value(%d) ` , m [ "b" ] . Value , n [ "b" ] . Value )
}
if m [ "c" ] . Value != 13 {
t . Fatalf ( ` n overwritten in m: m["c"].Value(%d) != n["c"].Value(%d) ` , m [ "c" ] . Value , n [ "c" ] . Value )
}
}
func TestYAMLMaps ( t * testing . T ) {
thing := loadYAML ( "testdata/thing.yml" )
license := loadYAML ( "testdata/license.yml" )
ft := thing [ "fields" ] . ( map [ interface { } ] interface { } )
fl := license [ "fields" ] . ( map [ interface { } ] interface { } )
2017-10-26 17:02:04 +02:00
// license has one extra field (site) and another already existing in thing (author) that Mergo won't override.
expectedLength := len ( ft ) + len ( fl ) - 1
2017-05-18 18:54:23 +02:00
if err := Merge ( & license , thing ) ; err != nil {
t . Fatal ( err . Error ( ) )
}
currentLength := len ( license [ "fields" ] . ( map [ interface { } ] interface { } ) )
if currentLength != expectedLength {
t . Fatalf ( ` thing not merged in license properly, license must have %d elements instead of %d ` , expectedLength , currentLength )
}
fields := license [ "fields" ] . ( map [ interface { } ] interface { } )
if _ , ok := fields [ "id" ] ; ! ok {
t . Fatalf ( ` thing not merged in license properly, license must have a new id field from thing ` )
}
}
func TestTwoPointerValues ( t * testing . T ) {
a := & simpleTest { }
b := & simpleTest { 42 }
if err := Merge ( a , b ) ; err != nil {
t . Fatalf ( ` Boom. You crossed the streams: %s ` , err )
}
}
func TestMap ( t * testing . T ) {
a := complexTest { }
a . ID = "athing"
c := moreComplextText { a , simpleTest { } , simpleTest { } }
b := map [ string ] interface { } {
"ct" : map [ string ] interface { } {
"st" : map [ string ] interface { } {
"value" : 42 ,
} ,
"sz" : 1 ,
"id" : "bthing" ,
} ,
"st" : & simpleTest { 144 } , // Mapping a reference
"zt" : simpleTest { 299 } , // Mapping a missing field (zt doesn't exist)
"nt" : simpleTest { 3 } ,
}
if err := Map ( & c , b ) ; err != nil {
t . FailNow ( )
}
m := b [ "ct" ] . ( map [ string ] interface { } )
n := m [ "st" ] . ( map [ string ] interface { } )
o := b [ "st" ] . ( * simpleTest )
p := b [ "nt" ] . ( simpleTest )
if c . Ct . St . Value != 42 {
t . Fatalf ( "b not merged in properly: c.Ct.St.Value(%d) != b.Ct.St.Value(%d)" , c . Ct . St . Value , n [ "value" ] )
}
if c . St . Value != 144 {
t . Fatalf ( "b not merged in properly: c.St.Value(%d) != b.St.Value(%d)" , c . St . Value , o . Value )
}
if c . Nt . Value != 3 {
t . Fatalf ( "b not merged in properly: c.Nt.Value(%d) != b.Nt.Value(%d)" , c . St . Value , p . Value )
}
if c . Ct . sz == 1 {
t . Fatalf ( "a's private field sz not preserved from merge: c.Ct.sz(%d) == b.Ct.sz(%d)" , c . Ct . sz , m [ "sz" ] )
}
if c . Ct . ID == m [ "id" ] {
t . Fatalf ( "a's field ID merged unexpectedly: c.Ct.ID(%s) == b.Ct.ID(%s)" , c . Ct . ID , m [ "id" ] )
}
}
func TestSimpleMap ( t * testing . T ) {
a := simpleTest { }
b := map [ string ] interface { } {
"value" : 42 ,
}
if err := Map ( & a , b ) ; err != nil {
t . FailNow ( )
}
if a . Value != 42 {
t . Fatalf ( "b not merged in properly: a.Value(%d) != b.Value(%v)" , a . Value , b [ "value" ] )
}
}
2017-10-26 17:02:04 +02:00
func TestIfcMap ( t * testing . T ) {
a := ifcTest { }
b := ifcTest { 42 }
if err := Map ( & a , b ) ; err != nil {
t . FailNow ( )
}
if a . I != 42 {
t . Fatalf ( "b not merged in properly: a.I(%d) != b.I(%d)" , a . I , b . I )
}
if ! reflect . DeepEqual ( a , b ) {
t . FailNow ( )
}
}
func TestIfcMapNoOverwrite ( t * testing . T ) {
a := ifcTest { 13 }
b := ifcTest { 42 }
if err := Map ( & a , b ) ; err != nil {
t . FailNow ( )
}
if a . I != 13 {
t . Fatalf ( "a not left alone: a.I(%d) == b.I(%d)" , a . I , b . I )
}
}
func TestIfcMapWithOverwrite ( t * testing . T ) {
a := ifcTest { 13 }
b := ifcTest { 42 }
if err := MapWithOverwrite ( & a , b ) ; err != nil {
t . FailNow ( )
}
if a . I != 42 {
t . Fatalf ( "b not merged in properly: a.I(%d) != b.I(%d)" , a . I , b . I )
}
if ! reflect . DeepEqual ( a , b ) {
t . FailNow ( )
}
}
2017-05-18 18:54:23 +02:00
type pointerMapTest struct {
A int
hidden int
B * simpleTest
}
func TestBackAndForth ( t * testing . T ) {
pt := pointerMapTest { 42 , 1 , & simpleTest { 66 } }
m := make ( map [ string ] interface { } )
if err := Map ( & m , pt ) ; err != nil {
t . FailNow ( )
}
var (
v interface { }
ok bool
)
if v , ok = m [ "a" ] ; v . ( int ) != pt . A || ! ok {
t . Fatalf ( "pt not merged in properly: m[`a`](%d) != pt.A(%d)" , v , pt . A )
}
if v , ok = m [ "b" ] ; ! ok {
t . Fatalf ( "pt not merged in properly: B is missing in m" )
}
var st * simpleTest
if st = v . ( * simpleTest ) ; st . Value != 66 {
t . Fatalf ( "something went wrong while mapping pt on m, B wasn't copied" )
}
bpt := pointerMapTest { }
if err := Map ( & bpt , m ) ; err != nil {
t . Fatal ( err )
}
if bpt . A != pt . A {
t . Fatalf ( "pt not merged in properly: bpt.A(%d) != pt.A(%d)" , bpt . A , pt . A )
}
if bpt . hidden == pt . hidden {
t . Fatalf ( "pt unexpectedly merged: bpt.hidden(%d) == pt.hidden(%d)" , bpt . hidden , pt . hidden )
}
if bpt . B . Value != pt . B . Value {
t . Fatalf ( "pt not merged in properly: bpt.B.Value(%d) != pt.B.Value(%d)" , bpt . B . Value , pt . B . Value )
}
}
2017-10-26 17:02:04 +02:00
func TestEmbeddedPointerUnpacking ( t * testing . T ) {
tests := [ ] struct { input pointerMapTest } {
{ pointerMapTest { 42 , 1 , nil } } ,
{ pointerMapTest { 42 , 1 , & simpleTest { 66 } } } ,
}
newValue := 77
m := map [ string ] interface { } {
"b" : map [ string ] interface { } {
"value" : newValue ,
} ,
}
for _ , test := range tests {
pt := test . input
if err := MapWithOverwrite ( & pt , m ) ; err != nil {
t . FailNow ( )
}
if pt . B . Value != newValue {
t . Fatalf ( "pt not mapped properly: pt.A.Value(%d) != m[`b`][`value`](%d)" , pt . B . Value , newValue )
}
}
}
2017-05-18 18:54:23 +02:00
type structWithTimePointer struct {
Birth * time . Time
}
func TestTime ( t * testing . T ) {
now := time . Now ( )
dataStruct := structWithTimePointer {
Birth : & now ,
}
dataMap := map [ string ] interface { } {
"Birth" : & now ,
}
b := structWithTimePointer { }
if err := Merge ( & b , dataStruct ) ; err != nil {
t . FailNow ( )
}
if b . Birth . IsZero ( ) {
t . Fatalf ( "time.Time not merged in properly: b.Birth(%v) != dataStruct['Birth'](%v)" , b . Birth , dataStruct . Birth )
}
if b . Birth != dataStruct . Birth {
t . Fatalf ( "time.Time not merged in properly: b.Birth(%v) != dataStruct['Birth'](%v)" , b . Birth , dataStruct . Birth )
}
b = structWithTimePointer { }
if err := Map ( & b , dataMap ) ; err != nil {
t . FailNow ( )
}
if b . Birth . IsZero ( ) {
t . Fatalf ( "time.Time not merged in properly: b.Birth(%v) != dataMap['Birth'](%v)" , b . Birth , dataMap [ "Birth" ] )
}
}
type simpleNested struct {
A int
}
type structWithNestedPtrValueMap struct {
NestedPtrValue map [ string ] * simpleNested
}
func TestNestedPtrValueInMap ( t * testing . T ) {
src := & structWithNestedPtrValueMap {
NestedPtrValue : map [ string ] * simpleNested {
"x" : {
A : 1 ,
} ,
} ,
}
dst := & structWithNestedPtrValueMap {
NestedPtrValue : map [ string ] * simpleNested {
"x" : { } ,
} ,
}
if err := Map ( dst , src ) ; err != nil {
t . FailNow ( )
}
if dst . NestedPtrValue [ "x" ] . A == 0 {
t . Fatalf ( "Nested Ptr value not merged in properly: dst.NestedPtrValue[\"x\"].A(%v) != src.NestedPtrValue[\"x\"].A(%v)" , dst . NestedPtrValue [ "x" ] . A , src . NestedPtrValue [ "x" ] . A )
}
}
func loadYAML ( path string ) ( m map [ string ] interface { } ) {
m = make ( map [ string ] interface { } )
raw , _ := ioutil . ReadFile ( path )
_ = yaml . Unmarshal ( raw , & m )
return
}
type structWithMap struct {
m map [ string ] structWithUnexportedProperty
}
type structWithUnexportedProperty struct {
s string
}
func TestUnexportedProperty ( t * testing . T ) {
a := structWithMap { map [ string ] structWithUnexportedProperty {
"key" : structWithUnexportedProperty { "hello" } ,
} }
b := structWithMap { map [ string ] structWithUnexportedProperty {
"key" : structWithUnexportedProperty { "hi" } ,
} }
defer func ( ) {
if r := recover ( ) ; r != nil {
t . Errorf ( "Should not have panicked" )
}
} ( )
Merge ( & a , b )
}
2017-10-26 17:02:04 +02:00
type structWithBoolPointer struct {
C * bool
}
func TestBooleanPointer ( t * testing . T ) {
bt , bf := true , false
src := structWithBoolPointer {
& bt ,
}
dst := structWithBoolPointer {
& bf ,
}
if err := Merge ( & dst , src ) ; err != nil {
t . FailNow ( )
}
if dst . C == src . C {
t . Fatalf ( "dst.C should be a different pointer than src.C" )
}
if * dst . C != * src . C {
t . Fatalf ( "dst.C should be true" )
}
}