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: ¶ms{ 443 Final: &final{ 444 Msg1: "from changes", 445 Msg2: "from changes", 446 }, 447 }, 448 }, 449 target: &config{ 450 Foo: "from target", 451 Params: ¶ms{ 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: ¶ms{ 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: ¶ms{ 485 Final: &final{ 486 Msg1: "from changes", 487 Msg2: "from changes", 488 }, 489 }, 490 }, 491 target: &config{ 492 Foo: "from target", 493 Params: ¶ms{ 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: ¶ms{ 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