1// Copyright 2013 Dario Castañé. All rights reserved.
2// Copyright 2009 The Go Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style
4// license that can be found in the LICENSE file.
5
6package mergo_test
7
8import (
9	"io/ioutil"
10	"reflect"
11	"strings"
12	"testing"
13	"time"
14
15	"github.com/imdario/mergo"
16	"gopkg.in/yaml.v2"
17)
18
19type simpleTest struct {
20	Value int
21}
22
23type complexTest struct {
24	St simpleTest
25	sz int
26	ID string
27}
28
29type mapTest struct {
30	M map[int]int
31}
32
33type ifcTest struct {
34	I interface{}
35}
36
37type moreComplextText struct {
38	Ct complexTest
39	St simpleTest
40	Nt simpleTest
41}
42
43type pointerTest struct {
44	C *simpleTest
45}
46
47type sliceTest struct {
48	S []int
49}
50
51func TestKb(t *testing.T) {
52	type testStruct struct {
53		Name     string
54		KeyValue map[string]interface{}
55	}
56
57	akv := make(map[string]interface{})
58	akv["Key1"] = "not value 1"
59	akv["Key2"] = "value2"
60	a := testStruct{}
61	a.Name = "A"
62	a.KeyValue = akv
63
64	bkv := make(map[string]interface{})
65	bkv["Key1"] = "value1"
66	bkv["Key3"] = "value3"
67	b := testStruct{}
68	b.Name = "B"
69	b.KeyValue = bkv
70
71	ekv := make(map[string]interface{})
72	ekv["Key1"] = "value1"
73	ekv["Key2"] = "value2"
74	ekv["Key3"] = "value3"
75	expected := testStruct{}
76	expected.Name = "B"
77	expected.KeyValue = ekv
78
79	if err := mergo.Merge(&b, a); err != nil {
80		t.Error(err)
81	}
82
83	if !reflect.DeepEqual(b, expected) {
84		t.Errorf("Actual: %#v did not match \nExpected: %#v", b, expected)
85	}
86}
87
88func TestNil(t *testing.T) {
89	if err := mergo.Merge(nil, nil); err != mergo.ErrNilArguments {
90		t.Fail()
91	}
92}
93
94func TestDifferentTypes(t *testing.T) {
95	a := simpleTest{42}
96	b := 42
97	if err := mergo.Merge(&a, b); err != mergo.ErrDifferentArgumentsTypes {
98		t.Fail()
99	}
100}
101
102func TestSimpleStruct(t *testing.T) {
103	a := simpleTest{}
104	b := simpleTest{42}
105	if err := mergo.Merge(&a, b); err != nil {
106		t.FailNow()
107	}
108	if a.Value != 42 {
109		t.Errorf("b not merged in properly: a.Value(%d) != b.Value(%d)", a.Value, b.Value)
110	}
111	if !reflect.DeepEqual(a, b) {
112		t.FailNow()
113	}
114}
115
116func TestComplexStruct(t *testing.T) {
117	a := complexTest{}
118	a.ID = "athing"
119	b := complexTest{simpleTest{42}, 1, "bthing"}
120	if err := mergo.Merge(&a, b); err != nil {
121		t.FailNow()
122	}
123	if a.St.Value != 42 {
124		t.Errorf("b not merged in properly: a.St.Value(%d) != b.St.Value(%d)", a.St.Value, b.St.Value)
125	}
126	if a.sz == 1 {
127		t.Errorf("a's private field sz not preserved from merge: a.sz(%d) == b.sz(%d)", a.sz, b.sz)
128	}
129	if a.ID == b.ID {
130		t.Errorf("a's field ID merged unexpectedly: a.ID(%s) == b.ID(%s)", a.ID, b.ID)
131	}
132}
133
134func TestComplexStructWithOverwrite(t *testing.T) {
135	a := complexTest{simpleTest{1}, 1, "do-not-overwrite-with-empty-value"}
136	b := complexTest{simpleTest{42}, 2, ""}
137
138	expect := complexTest{simpleTest{42}, 1, "do-not-overwrite-with-empty-value"}
139	if err := mergo.MergeWithOverwrite(&a, b); err != nil {
140		t.FailNow()
141	}
142
143	if !reflect.DeepEqual(a, expect) {
144		t.Errorf("Test failed:\ngot  :\n%#v\n\nwant :\n%#v\n\n", a, expect)
145	}
146}
147
148func TestPointerStruct(t *testing.T) {
149	s1 := simpleTest{}
150	s2 := simpleTest{19}
151	a := pointerTest{&s1}
152	b := pointerTest{&s2}
153	if err := mergo.Merge(&a, b); err != nil {
154		t.FailNow()
155	}
156	if a.C.Value != b.C.Value {
157		t.Errorf("b not merged in properly: a.C.Value(%d) != b.C.Value(%d)", a.C.Value, b.C.Value)
158	}
159}
160
161type embeddingStruct struct {
162	embeddedStruct
163}
164
165type embeddedStruct struct {
166	A string
167}
168
169func TestEmbeddedStruct(t *testing.T) {
170	tests := []struct {
171		src      embeddingStruct
172		dst      embeddingStruct
173		expected embeddingStruct
174	}{
175		{
176			src: embeddingStruct{
177				embeddedStruct{"foo"},
178			},
179			dst: embeddingStruct{
180				embeddedStruct{""},
181			},
182			expected: embeddingStruct{
183				embeddedStruct{"foo"},
184			},
185		},
186		{
187			src: embeddingStruct{
188				embeddedStruct{""},
189			},
190			dst: embeddingStruct{
191				embeddedStruct{"bar"},
192			},
193			expected: embeddingStruct{
194				embeddedStruct{"bar"},
195			},
196		},
197		{
198			src: embeddingStruct{
199				embeddedStruct{"foo"},
200			},
201			dst: embeddingStruct{
202				embeddedStruct{"bar"},
203			},
204			expected: embeddingStruct{
205				embeddedStruct{"bar"},
206			},
207		},
208	}
209
210	for _, test := range tests {
211		err := mergo.Merge(&test.dst, test.src)
212		if err != nil {
213			t.Errorf("unexpected error: %v", err)
214			continue
215		}
216		if !reflect.DeepEqual(test.dst, test.expected) {
217			t.Errorf("unexpected output\nexpected:\n%+v\nsaw:\n%+v\n", test.expected, test.dst)
218		}
219	}
220}
221
222func TestPointerStructNil(t *testing.T) {
223	a := pointerTest{nil}
224	b := pointerTest{&simpleTest{19}}
225	if err := mergo.Merge(&a, b); err != nil {
226		t.FailNow()
227	}
228	if a.C.Value != b.C.Value {
229		t.Errorf("b not merged in a properly: a.C.Value(%d) != b.C.Value(%d)", a.C.Value, b.C.Value)
230	}
231}
232
233func testSlice(t *testing.T, a []int, b []int, e []int, opts ...func(*mergo.Config)) {
234	t.Helper()
235	bc := b
236
237	sa := sliceTest{a}
238	sb := sliceTest{b}
239	if err := mergo.Merge(&sa, sb, opts...); err != nil {
240		t.FailNow()
241	}
242	if !reflect.DeepEqual(sb.S, bc) {
243		t.Errorf("Source slice was modified %d != %d", sb.S, bc)
244	}
245	if !reflect.DeepEqual(sa.S, e) {
246		t.Errorf("b not merged in a proper way %d != %d", sa.S, e)
247	}
248
249	ma := map[string][]int{"S": a}
250	mb := map[string][]int{"S": b}
251	if err := mergo.Merge(&ma, mb, opts...); err != nil {
252		t.FailNow()
253	}
254	if !reflect.DeepEqual(mb["S"], bc) {
255		t.Errorf("map value: Source slice was modified %d != %d", mb["S"], bc)
256	}
257	if !reflect.DeepEqual(ma["S"], e) {
258		t.Errorf("map value: b not merged in a proper way %d != %d", ma["S"], e)
259	}
260
261	if a == nil {
262		// test case with missing dst key
263		ma := map[string][]int{}
264		mb := map[string][]int{"S": b}
265		if err := mergo.Merge(&ma, mb); err != nil {
266			t.FailNow()
267		}
268		if !reflect.DeepEqual(mb["S"], bc) {
269			t.Errorf("missing dst key: Source slice was modified %d != %d", mb["S"], bc)
270		}
271		if !reflect.DeepEqual(ma["S"], e) {
272			t.Errorf("missing dst key: b not merged in a proper way %d != %d", ma["S"], e)
273		}
274	}
275
276	if b == nil {
277		// test case with missing src key
278		ma := map[string][]int{"S": a}
279		mb := map[string][]int{}
280		if err := mergo.Merge(&ma, mb); err != nil {
281			t.FailNow()
282		}
283		if !reflect.DeepEqual(mb["S"], bc) {
284			t.Errorf("missing src key: Source slice was modified %d != %d", mb["S"], bc)
285		}
286		if !reflect.DeepEqual(ma["S"], e) {
287			t.Errorf("missing src key: b not merged in a proper way %d != %d", ma["S"], e)
288		}
289	}
290}
291
292func TestSlice(t *testing.T) {
293	testSlice(t, nil, []int{1, 2, 3}, []int{1, 2, 3})
294	testSlice(t, []int{}, []int{1, 2, 3}, []int{1, 2, 3})
295	testSlice(t, []int{1}, []int{2, 3}, []int{1})
296	testSlice(t, []int{1}, []int{}, []int{1})
297	testSlice(t, []int{1}, nil, []int{1})
298	testSlice(t, nil, []int{1, 2, 3}, []int{1, 2, 3}, mergo.WithAppendSlice)
299	testSlice(t, []int{}, []int{1, 2, 3}, []int{1, 2, 3}, mergo.WithAppendSlice)
300	testSlice(t, []int{1}, []int{2, 3}, []int{1, 2, 3}, mergo.WithAppendSlice)
301	testSlice(t, []int{1}, []int{2, 3}, []int{1, 2, 3}, mergo.WithAppendSlice, mergo.WithOverride)
302	testSlice(t, []int{1}, []int{}, []int{1}, mergo.WithAppendSlice)
303	testSlice(t, []int{1}, nil, []int{1}, mergo.WithAppendSlice)
304}
305
306func TestEmptyMaps(t *testing.T) {
307	a := mapTest{}
308	b := mapTest{
309		map[int]int{},
310	}
311	if err := mergo.Merge(&a, b); err != nil {
312		t.Fail()
313	}
314	if !reflect.DeepEqual(a, b) {
315		t.FailNow()
316	}
317}
318
319func TestEmptyToEmptyMaps(t *testing.T) {
320	a := mapTest{}
321	b := mapTest{}
322	if err := mergo.Merge(&a, b); err != nil {
323		t.Fail()
324	}
325	if !reflect.DeepEqual(a, b) {
326		t.FailNow()
327	}
328}
329
330func TestEmptyToNotEmptyMaps(t *testing.T) {
331	a := mapTest{map[int]int{
332		1: 2,
333		3: 4,
334	}}
335	aa := mapTest{map[int]int{
336		1: 2,
337		3: 4,
338	}}
339	b := mapTest{
340		map[int]int{},
341	}
342	if err := mergo.Merge(&a, b); err != nil {
343		t.Fail()
344	}
345	if !reflect.DeepEqual(a, aa) {
346		t.FailNow()
347	}
348}
349
350func TestMapsWithOverwrite(t *testing.T) {
351	m := map[string]simpleTest{
352		"a": {},   // overwritten by 16
353		"b": {42}, // overwritten by 0, as map Value is not addressable and it doesn't check for b is set or not set in `n`
354		"c": {13}, // overwritten by 12
355		"d": {61},
356	}
357	n := map[string]simpleTest{
358		"a": {16},
359		"b": {},
360		"c": {12},
361		"e": {14},
362	}
363	expect := map[string]simpleTest{
364		"a": {16},
365		"b": {},
366		"c": {12},
367		"d": {61},
368		"e": {14},
369	}
370
371	if err := mergo.MergeWithOverwrite(&m, n); err != nil {
372		t.Errorf(err.Error())
373	}
374
375	if !reflect.DeepEqual(m, expect) {
376		t.Errorf("Test failed:\ngot  :\n%#v\n\nwant :\n%#v\n\n", m, expect)
377	}
378}
379
380func TestMapWithEmbeddedStructPointer(t *testing.T) {
381	m := map[string]*simpleTest{
382		"a": {},   // overwritten by 16
383		"b": {42}, // not overwritten by empty value
384		"c": {13}, // overwritten by 12
385		"d": {61},
386	}
387	n := map[string]*simpleTest{
388		"a": {16},
389		"b": {},
390		"c": {12},
391		"e": {14},
392	}
393	expect := map[string]*simpleTest{
394		"a": {16},
395		"b": {42},
396		"c": {12},
397		"d": {61},
398		"e": {14},
399	}
400
401	if err := mergo.Merge(&m, n, mergo.WithOverride); err != nil {
402		t.Errorf(err.Error())
403	}
404
405	if !reflect.DeepEqual(m, expect) {
406		t.Errorf("Test failed:\ngot  :\n%#v\n\nwant :\n%#v\n\n", m, expect)
407	}
408}
409
410func TestMergeUsingStructAndMap(t *testing.T) {
411	type multiPtr struct {
412		Text   string
413		Number int
414	}
415	type final struct {
416		Msg1 string
417		Msg2 string
418	}
419	type params struct {
420		Name  string
421		Multi *multiPtr
422		Final *final
423	}
424	type config struct {
425		Foo    string
426		Bar    string
427		Params *params
428	}
429
430	cases := []struct {
431		name      string
432		overwrite bool
433		changes   *config
434		target    *config
435		output    *config
436	}{
437		{
438			name:      "Should overwrite values in target for non-nil values in source",
439			overwrite: true,
440			changes: &config{
441				Bar: "from changes",
442				Params: &params{
443					Final: &final{
444						Msg1: "from changes",
445						Msg2: "from changes",
446					},
447				},
448			},
449			target: &config{
450				Foo: "from target",
451				Params: &params{
452					Name: "from target",
453					Multi: &multiPtr{
454						Text:   "from target",
455						Number: 5,
456					},
457					Final: &final{
458						Msg1: "from target",
459						Msg2: "",
460					},
461				},
462			},
463			output: &config{
464				Foo: "from target",
465				Bar: "from changes",
466				Params: &params{
467					Name: "from target",
468					Multi: &multiPtr{
469						Text:   "from target",
470						Number: 5,
471					},
472					Final: &final{
473						Msg1: "from changes",
474						Msg2: "from changes",
475					},
476				},
477			},
478		},
479		{
480			name:      "Should not overwrite values in target for non-nil values in source",
481			overwrite: false,
482			changes: &config{
483				Bar: "from changes",
484				Params: &params{
485					Final: &final{
486						Msg1: "from changes",
487						Msg2: "from changes",
488					},
489				},
490			},
491			target: &config{
492				Foo: "from target",
493				Params: &params{
494					Name: "from target",
495					Multi: &multiPtr{
496						Text:   "from target",
497						Number: 5,
498					},
499					Final: &final{
500						Msg1: "from target",
501						Msg2: "",
502					},
503				},
504			},
505			output: &config{
506				Foo: "from target",
507				Bar: "from changes",
508				Params: &params{
509					Name: "from target",
510					Multi: &multiPtr{
511						Text:   "from target",
512						Number: 5,
513					},
514					Final: &final{
515						Msg1: "from target",
516						Msg2: "from changes",
517					},
518				},
519			},
520		},
521	}
522
523	for _, tc := range cases {
524		t.Run(tc.name, func(t *testing.T) {
525			var err error
526			if tc.overwrite {
527				err = mergo.Merge(tc.target, *tc.changes, mergo.WithOverride)
528			} else {
529				err = mergo.Merge(tc.target, *tc.changes)
530			}
531			if err != nil {
532				t.Error(err)
533			}
534			if !reflect.DeepEqual(tc.target, tc.output) {
535				t.Errorf("Test failed:\ngot  :\n%+v\n\nwant :\n%+v\n\n", tc.target.Params, tc.output.Params)
536			}
537		})
538	}
539}
540func TestMaps(t *testing.T) {
541	m := map[string]simpleTest{
542		"a": {},
543		"b": {42},
544		"c": {13},
545		"d": {61},
546	}
547	n := map[string]simpleTest{
548		"a": {16},
549		"b": {},
550		"c": {12},
551		"e": {14},
552	}
553	expect := map[string]simpleTest{
554		"a": {0},
555		"b": {42},
556		"c": {13},
557		"d": {61},
558		"e": {14},
559	}
560
561	if err := mergo.Merge(&m, n); err != nil {
562		t.Errorf(err.Error())
563	}
564
565	if !reflect.DeepEqual(m, expect) {
566		t.Errorf("Test failed:\ngot  :\n%#v\n\nwant :\n%#v\n\n", m, expect)
567	}
568	if m["a"].Value != 0 {
569		t.Errorf(`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)
570	}
571	if m["b"].Value != 42 {
572		t.Errorf(`n wrongly merged in m: m["b"].Value(%d) != n["b"].Value(%d)`, m["b"].Value, n["b"].Value)
573	}
574	if m["c"].Value != 13 {
575		t.Errorf(`n overwritten in m: m["c"].Value(%d) != n["c"].Value(%d)`, m["c"].Value, n["c"].Value)
576	}
577}
578
579func TestMapsWithNilPointer(t *testing.T) {
580	m := map[string]*simpleTest{
581		"a": nil,
582		"b": nil,
583	}
584	n := map[string]*simpleTest{
585		"b": nil,
586		"c": nil,
587	}
588	expect := map[string]*simpleTest{
589		"a": nil,
590		"b": nil,
591		"c": nil,
592	}
593
594	if err := mergo.Merge(&m, n, mergo.WithOverride); err != nil {
595		t.Errorf(err.Error())
596	}
597
598	if !reflect.DeepEqual(m, expect) {
599		t.Errorf("Test failed:\ngot   :\n%#v\n\nwant :\n%#v\n\n", m, expect)
600	}
601}
602
603func TestYAMLMaps(t *testing.T) {
604	thing := loadYAML("testdata/thing.yml")
605	license := loadYAML("testdata/license.yml")
606	ft := thing["fields"].(map[interface{}]interface{})
607	fl := license["fields"].(map[interface{}]interface{})
608	// license has one extra field (site) and another already existing in thing (author) that Mergo won't override.
609	expectedLength := len(ft) + len(fl) - 1
610	if err := mergo.Merge(&license, thing); err != nil {
611		t.Error(err.Error())
612	}
613	currentLength := len(license["fields"].(map[interface{}]interface{}))
614	if currentLength != expectedLength {
615		t.Errorf(`thing not merged in license properly, license must have %d elements instead of %d`, expectedLength, currentLength)
616	}
617	fields := license["fields"].(map[interface{}]interface{})
618	if _, ok := fields["id"]; !ok {
619		t.Errorf(`thing not merged in license properly, license must have a new id field from thing`)
620	}
621}
622
623func TestTwoPointerValues(t *testing.T) {
624	a := &simpleTest{}
625	b := &simpleTest{42}
626	if err := mergo.Merge(a, b); err != nil {
627		t.Errorf(`Boom. You crossed the streams: %s`, err)
628	}
629}
630
631func TestMap(t *testing.T) {
632	a := complexTest{}
633	a.ID = "athing"
634	c := moreComplextText{a, simpleTest{}, simpleTest{}}
635	b := map[string]interface{}{
636		"ct": map[string]interface{}{
637			"st": map[string]interface{}{
638				"value": 42,
639			},
640			"sz": 1,
641			"id": "bthing",
642		},
643		"st": &simpleTest{144}, // Mapping a reference
644		"zt": simpleTest{299},  // Mapping a missing field (zt doesn't exist)
645		"nt": simpleTest{3},
646	}
647	if err := mergo.Map(&c, b); err != nil {
648		t.FailNow()
649	}
650	m := b["ct"].(map[string]interface{})
651	n := m["st"].(map[string]interface{})
652	o := b["st"].(*simpleTest)
653	p := b["nt"].(simpleTest)
654	if c.Ct.St.Value != 42 {
655		t.Errorf("b not merged in properly: c.Ct.St.Value(%d) != b.Ct.St.Value(%d)", c.Ct.St.Value, n["value"])
656	}
657	if c.St.Value != 144 {
658		t.Errorf("b not merged in properly: c.St.Value(%d) != b.St.Value(%d)", c.St.Value, o.Value)
659	}
660	if c.Nt.Value != 3 {
661		t.Errorf("b not merged in properly: c.Nt.Value(%d) != b.Nt.Value(%d)", c.St.Value, p.Value)
662	}
663	if c.Ct.sz == 1 {
664		t.Errorf("a's private field sz not preserved from merge: c.Ct.sz(%d) == b.Ct.sz(%d)", c.Ct.sz, m["sz"])
665	}
666	if c.Ct.ID == m["id"] {
667		t.Errorf("a's field ID merged unexpectedly: c.Ct.ID(%s) == b.Ct.ID(%s)", c.Ct.ID, m["id"])
668	}
669}
670
671func TestSimpleMap(t *testing.T) {
672	a := simpleTest{}
673	b := map[string]interface{}{
674		"value": 42,
675	}
676	if err := mergo.Map(&a, b); err != nil {
677		t.FailNow()
678	}
679	if a.Value != 42 {
680		t.Errorf("b not merged in properly: a.Value(%d) != b.Value(%v)", a.Value, b["value"])
681	}
682}
683
684func TestIfcMap(t *testing.T) {
685	a := ifcTest{}
686	b := ifcTest{42}
687	if err := mergo.Map(&a, b); err != nil {
688		t.FailNow()
689	}
690	if a.I != 42 {
691		t.Errorf("b not merged in properly: a.I(%d) != b.I(%d)", a.I, b.I)
692	}
693	if !reflect.DeepEqual(a, b) {
694		t.FailNow()
695	}
696}
697
698func TestIfcMapNoOverwrite(t *testing.T) {
699	a := ifcTest{13}
700	b := ifcTest{42}
701	if err := mergo.Map(&a, b); err != nil {
702		t.FailNow()
703	}
704	if a.I != 13 {
705		t.Errorf("a not left alone: a.I(%d) == b.I(%d)", a.I, b.I)
706	}
707}
708
709func TestIfcMapWithOverwrite(t *testing.T) {
710	a := ifcTest{13}
711	b := ifcTest{42}
712	if err := mergo.MapWithOverwrite(&a, b); err != nil {
713		t.FailNow()
714	}
715	if a.I != 42 {
716		t.Errorf("b not merged in properly: a.I(%d) != b.I(%d)", a.I, b.I)
717	}
718	if !reflect.DeepEqual(a, b) {
719		t.FailNow()
720	}
721}
722
723type pointerMapTest struct {
724	A      int
725	hidden int
726	B      *simpleTest
727}
728
729func TestBackAndForth(t *testing.T) {
730	pt := pointerMapTest{42, 1, &simpleTest{66}}
731	m := make(map[string]interface{})
732	if err := mergo.Map(&m, pt); err != nil {
733		t.FailNow()
734	}
735	var (
736		v  interface{}
737		ok bool
738	)
739	if v, ok = m["a"]; v.(int) != pt.A || !ok {
740		t.Errorf("pt not merged in properly: m[`a`](%d) != pt.A(%d)", v, pt.A)
741	}
742	if v, ok = m["b"]; !ok {
743		t.Errorf("pt not merged in properly: B is missing in m")
744	}
745	var st *simpleTest
746	if st = v.(*simpleTest); st.Value != 66 {
747		t.Errorf("something went wrong while mapping pt on m, B wasn't copied")
748	}
749	bpt := pointerMapTest{}
750	if err := mergo.Map(&bpt, m); err != nil {
751		t.Error(err)
752	}
753	if bpt.A != pt.A {
754		t.Errorf("pt not merged in properly: bpt.A(%d) != pt.A(%d)", bpt.A, pt.A)
755	}
756	if bpt.hidden == pt.hidden {
757		t.Errorf("pt unexpectedly merged: bpt.hidden(%d) == pt.hidden(%d)", bpt.hidden, pt.hidden)
758	}
759	if bpt.B.Value != pt.B.Value {
760		t.Errorf("pt not merged in properly: bpt.B.Value(%d) != pt.B.Value(%d)", bpt.B.Value, pt.B.Value)
761	}
762}
763
764func TestEmbeddedPointerUnpacking(t *testing.T) {
765	tests := []struct{ input pointerMapTest }{
766		{pointerMapTest{42, 1, nil}},
767		{pointerMapTest{42, 1, &simpleTest{66}}},
768	}
769	newValue := 77
770	m := map[string]interface{}{
771		"b": map[string]interface{}{
772			"value": newValue,
773		},
774	}
775	for _, test := range tests {
776		pt := test.input
777		if err := mergo.MapWithOverwrite(&pt, m); err != nil {
778			t.FailNow()
779		}
780		if pt.B.Value != newValue {
781			t.Errorf("pt not mapped properly: pt.A.Value(%d) != m[`b`][`value`](%d)", pt.B.Value, newValue)
782		}
783
784	}
785}
786
787type structWithTimePointer struct {
788	Birth *time.Time
789}
790
791func TestTime(t *testing.T) {
792	now := time.Now()
793	dataStruct := structWithTimePointer{
794		Birth: &now,
795	}
796	dataMap := map[string]interface{}{
797		"Birth": &now,
798	}
799	b := structWithTimePointer{}
800	if err := mergo.Merge(&b, dataStruct); err != nil {
801		t.FailNow()
802	}
803	if b.Birth.IsZero() {
804		t.Errorf("time.Time not merged in properly: b.Birth(%v) != dataStruct['Birth'](%v)", b.Birth, dataStruct.Birth)
805	}
806	if b.Birth != dataStruct.Birth {
807		t.Errorf("time.Time not merged in properly: b.Birth(%v) != dataStruct['Birth'](%v)", b.Birth, dataStruct.Birth)
808	}
809	b = structWithTimePointer{}
810	if err := mergo.Map(&b, dataMap); err != nil {
811		t.FailNow()
812	}
813	if b.Birth.IsZero() {
814		t.Errorf("time.Time not merged in properly: b.Birth(%v) != dataMap['Birth'](%v)", b.Birth, dataMap["Birth"])
815	}
816}
817
818type simpleNested struct {
819	A int
820}
821
822type structWithNestedPtrValueMap struct {
823	NestedPtrValue map[string]*simpleNested
824}
825
826func TestNestedPtrValueInMap(t *testing.T) {
827	src := &structWithNestedPtrValueMap{
828		NestedPtrValue: map[string]*simpleNested{
829			"x": {
830				A: 1,
831			},
832		},
833	}
834	dst := &structWithNestedPtrValueMap{
835		NestedPtrValue: map[string]*simpleNested{
836			"x": {},
837		},
838	}
839	if err := mergo.Map(dst, src); err != nil {
840		t.FailNow()
841	}
842	if dst.NestedPtrValue["x"].A == 0 {
843		t.Errorf("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)
844	}
845}
846
847func loadYAML(path string) (m map[string]interface{}) {
848	m = make(map[string]interface{})
849	raw, _ := ioutil.ReadFile(path)
850	_ = yaml.Unmarshal(raw, &m)
851	return
852}
853
854type structWithMap struct {
855	m map[string]structWithUnexportedProperty
856}
857
858type structWithUnexportedProperty struct {
859	s string
860}
861
862func TestUnexportedProperty(t *testing.T) {
863	a := structWithMap{map[string]structWithUnexportedProperty{
864		"key": {"hello"},
865	}}
866	b := structWithMap{map[string]structWithUnexportedProperty{
867		"key": {"hi"},
868	}}
869	defer func() {
870		if r := recover(); r != nil {
871			t.Errorf("Should not have panicked")
872		}
873	}()
874	mergo.Merge(&a, b)
875}
876
877type structWithBoolPointer struct {
878	C *bool
879}
880
881func TestBooleanPointer(t *testing.T) {
882	bt, bf := true, false
883	src := structWithBoolPointer{
884		&bt,
885	}
886	dst := structWithBoolPointer{
887		&bf,
888	}
889	if err := mergo.Merge(&dst, src); err != nil {
890		t.FailNow()
891	}
892	if dst.C == src.C {
893		t.Errorf("dst.C should be a different pointer than src.C")
894	}
895	if *dst.C != *src.C {
896		t.Errorf("dst.C should be true")
897	}
898}
899
900func TestMergeMapWithInnerSliceOfDifferentType(t *testing.T) {
901	testCases := []struct {
902		name    string
903		options []func(*mergo.Config)
904		err     string
905	}{
906		{
907			"With override and append slice",
908			[]func(*mergo.Config){mergo.WithOverride, mergo.WithAppendSlice},
909			"cannot append two slices with different type",
910		},
911		{
912			"With override and type check",
913			[]func(*mergo.Config){mergo.WithOverride, mergo.WithTypeCheck},
914			"cannot override two slices with different type",
915		},
916	}
917	for _, tc := range testCases {
918		t.Run(tc.name, func(t *testing.T) {
919			src := map[string]interface{}{
920				"foo": []string{"a", "b"},
921			}
922			dst := map[string]interface{}{
923				"foo": []int{1, 2},
924			}
925
926			if err := mergo.Merge(&src, &dst, tc.options...); err == nil || !strings.Contains(err.Error(), tc.err) {
927				t.Errorf("expected %q, got %q", tc.err, err)
928			}
929		})
930	}
931}
932
933func TestMergeSlicesIsNotSupported(t *testing.T) {
934	src := []string{"a", "b"}
935	dst := []int{1, 2}
936
937	if err := mergo.Merge(&src, &dst, mergo.WithOverride, mergo.WithAppendSlice); err != mergo.ErrNotSupported {
938		t.Errorf("expected %q, got %q", mergo.ErrNotSupported, err)
939	}
940}
941