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 7 8import ( 9 "gopkg.in/yaml.v2" 10 "io/ioutil" 11 "reflect" 12 "testing" 13 "time" 14) 15 16type simpleTest struct { 17 Value int 18} 19 20type complexTest struct { 21 St simpleTest 22 sz int 23 ID string 24} 25 26type mapTest struct { 27 M map[int]int 28} 29 30type ifcTest struct { 31 I interface{} 32} 33 34type moreComplextText struct { 35 Ct complexTest 36 St simpleTest 37 Nt simpleTest 38} 39 40type pointerTest struct { 41 C *simpleTest 42} 43 44type sliceTest struct { 45 S []int 46} 47 48func TestKb(t *testing.T) { 49 type testStruct struct { 50 Name string 51 KeyValue map[string]interface{} 52 } 53 54 akv := make(map[string]interface{}) 55 akv["Key1"] = "not value 1" 56 akv["Key2"] = "value2" 57 a := testStruct{} 58 a.Name = "A" 59 a.KeyValue = akv 60 61 bkv := make(map[string]interface{}) 62 bkv["Key1"] = "value1" 63 bkv["Key3"] = "value3" 64 b := testStruct{} 65 b.Name = "B" 66 b.KeyValue = bkv 67 68 ekv := make(map[string]interface{}) 69 ekv["Key1"] = "value1" 70 ekv["Key2"] = "value2" 71 ekv["Key3"] = "value3" 72 expected := testStruct{} 73 expected.Name = "B" 74 expected.KeyValue = ekv 75 76 Merge(&b, a) 77 78 if !reflect.DeepEqual(b, expected) { 79 t.Errorf("Actual: %#v did not match \nExpected: %#v", b, expected) 80 } 81} 82 83func TestNil(t *testing.T) { 84 if err := Merge(nil, nil); err != ErrNilArguments { 85 t.Fail() 86 } 87} 88 89func TestDifferentTypes(t *testing.T) { 90 a := simpleTest{42} 91 b := 42 92 if err := Merge(&a, b); err != ErrDifferentArgumentsTypes { 93 t.Fail() 94 } 95} 96 97func TestSimpleStruct(t *testing.T) { 98 a := simpleTest{} 99 b := simpleTest{42} 100 if err := Merge(&a, b); err != nil { 101 t.FailNow() 102 } 103 if a.Value != 42 { 104 t.Fatalf("b not merged in properly: a.Value(%d) != b.Value(%d)", a.Value, b.Value) 105 } 106 if !reflect.DeepEqual(a, b) { 107 t.FailNow() 108 } 109} 110 111func TestComplexStruct(t *testing.T) { 112 a := complexTest{} 113 a.ID = "athing" 114 b := complexTest{simpleTest{42}, 1, "bthing"} 115 if err := Merge(&a, b); err != nil { 116 t.FailNow() 117 } 118 if a.St.Value != 42 { 119 t.Fatalf("b not merged in properly: a.St.Value(%d) != b.St.Value(%d)", a.St.Value, b.St.Value) 120 } 121 if a.sz == 1 { 122 t.Fatalf("a's private field sz not preserved from merge: a.sz(%d) == b.sz(%d)", a.sz, b.sz) 123 } 124 if a.ID == b.ID { 125 t.Fatalf("a's field ID merged unexpectedly: a.ID(%s) == b.ID(%s)", a.ID, b.ID) 126 } 127} 128 129func TestComplexStructWithOverwrite(t *testing.T) { 130 a := complexTest{simpleTest{1}, 1, "do-not-overwrite-with-empty-value"} 131 b := complexTest{simpleTest{42}, 2, ""} 132 133 expect := complexTest{simpleTest{42}, 1, "do-not-overwrite-with-empty-value"} 134 if err := MergeWithOverwrite(&a, b); err != nil { 135 t.FailNow() 136 } 137 138 if !reflect.DeepEqual(a, expect) { 139 t.Fatalf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", a, expect) 140 } 141} 142 143func TestPointerStruct(t *testing.T) { 144 s1 := simpleTest{} 145 s2 := simpleTest{19} 146 a := pointerTest{&s1} 147 b := pointerTest{&s2} 148 if err := Merge(&a, b); err != nil { 149 t.FailNow() 150 } 151 if a.C.Value != b.C.Value { 152 t.Fatalf("b not merged in properly: a.C.Value(%d) != b.C.Value(%d)", a.C.Value, b.C.Value) 153 } 154} 155 156type embeddingStruct struct { 157 embeddedStruct 158} 159 160type embeddedStruct struct { 161 A string 162} 163 164func TestEmbeddedStruct(t *testing.T) { 165 tests := []struct { 166 src embeddingStruct 167 dst embeddingStruct 168 expected embeddingStruct 169 }{ 170 { 171 src: embeddingStruct{ 172 embeddedStruct{"foo"}, 173 }, 174 dst: embeddingStruct{ 175 embeddedStruct{""}, 176 }, 177 expected: embeddingStruct{ 178 embeddedStruct{"foo"}, 179 }, 180 }, 181 { 182 src: embeddingStruct{ 183 embeddedStruct{""}, 184 }, 185 dst: embeddingStruct{ 186 embeddedStruct{"bar"}, 187 }, 188 expected: embeddingStruct{ 189 embeddedStruct{"bar"}, 190 }, 191 }, 192 { 193 src: embeddingStruct{ 194 embeddedStruct{"foo"}, 195 }, 196 dst: embeddingStruct{ 197 embeddedStruct{"bar"}, 198 }, 199 expected: embeddingStruct{ 200 embeddedStruct{"bar"}, 201 }, 202 }, 203 } 204 205 for _, test := range tests { 206 err := Merge(&test.dst, test.src) 207 if err != nil { 208 t.Errorf("unexpected error: %v", err) 209 continue 210 } 211 if !reflect.DeepEqual(test.dst, test.expected) { 212 t.Errorf("unexpected output\nexpected:\n%+v\nsaw:\n%+v\n", test.expected, test.dst) 213 } 214 } 215} 216 217func TestPointerStructNil(t *testing.T) { 218 a := pointerTest{nil} 219 b := pointerTest{&simpleTest{19}} 220 if err := Merge(&a, b); err != nil { 221 t.FailNow() 222 } 223 if a.C.Value != b.C.Value { 224 t.Fatalf("b not merged in a properly: a.C.Value(%d) != b.C.Value(%d)", a.C.Value, b.C.Value) 225 } 226} 227 228func testSlice(t *testing.T, a []int, b []int) { 229 bc := b 230 e := append(a, b...) 231 232 sa := sliceTest{a} 233 sb := sliceTest{b} 234 if err := Merge(&sa, sb); err != nil { 235 t.FailNow() 236 } 237 if !reflect.DeepEqual(sb.S, bc) { 238 t.Fatalf("Source slice was modified %d != %d", sb.S, bc) 239 } 240 if !reflect.DeepEqual(sa.S, e) { 241 t.Fatalf("b not merged in a proper way %d != %d", sa.S, e) 242 } 243 244 ma := map[string][]int{"S": a} 245 mb := map[string][]int{"S": b} 246 if err := Merge(&ma, mb); err != nil { 247 t.FailNow() 248 } 249 if !reflect.DeepEqual(mb["S"], bc) { 250 t.Fatalf("Source slice was modified %d != %d", mb["S"], bc) 251 } 252 if !reflect.DeepEqual(ma["S"], e) { 253 t.Fatalf("b not merged in a proper way %d != %d", ma["S"], e) 254 } 255 256 if a == nil { 257 // test case with missing dst key 258 ma := map[string][]int{} 259 mb := map[string][]int{"S": b} 260 if err := Merge(&ma, mb); err != nil { 261 t.FailNow() 262 } 263 if !reflect.DeepEqual(mb["S"], bc) { 264 t.Fatalf("Source slice was modified %d != %d", mb["S"], bc) 265 } 266 if !reflect.DeepEqual(ma["S"], e) { 267 t.Fatalf("b not merged in a proper way %d != %d", ma["S"], e) 268 } 269 } 270 271 if b == nil { 272 // test case with missing src key 273 ma := map[string][]int{"S": a} 274 mb := map[string][]int{} 275 if err := Merge(&ma, mb); err != nil { 276 t.FailNow() 277 } 278 if !reflect.DeepEqual(mb["S"], bc) { 279 t.Fatalf("Source slice was modified %d != %d", mb["S"], bc) 280 } 281 if !reflect.DeepEqual(ma["S"], e) { 282 t.Fatalf("b not merged in a proper way %d != %d", ma["S"], e) 283 } 284 } 285} 286 287func TestSlice(t *testing.T) { 288 testSlice(t, nil, []int{1, 2, 3}) 289 testSlice(t, []int{}, []int{1, 2, 3}) 290 testSlice(t, []int{1}, []int{2, 3}) 291 testSlice(t, []int{1}, []int{}) 292 testSlice(t, []int{1}, nil) 293} 294 295func TestEmptyMaps(t *testing.T) { 296 a := mapTest{} 297 b := mapTest{ 298 map[int]int{}, 299 } 300 if err := Merge(&a, b); err != nil { 301 t.Fail() 302 } 303 if !reflect.DeepEqual(a, b) { 304 t.FailNow() 305 } 306} 307 308func TestEmptyToEmptyMaps(t *testing.T) { 309 a := mapTest{} 310 b := mapTest{} 311 if err := Merge(&a, b); err != nil { 312 t.Fail() 313 } 314 if !reflect.DeepEqual(a, b) { 315 t.FailNow() 316 } 317} 318 319func TestEmptyToNotEmptyMaps(t *testing.T) { 320 a := mapTest{map[int]int{ 321 1: 2, 322 3: 4, 323 }} 324 aa := mapTest{map[int]int{ 325 1: 2, 326 3: 4, 327 }} 328 b := mapTest{ 329 map[int]int{}, 330 } 331 if err := Merge(&a, b); err != nil { 332 t.Fail() 333 } 334 if !reflect.DeepEqual(a, aa) { 335 t.FailNow() 336 } 337} 338 339func TestMapsWithOverwrite(t *testing.T) { 340 m := map[string]simpleTest{ 341 "a": {}, // overwritten by 16 342 "b": {42}, // not overwritten by empty value 343 "c": {13}, // overwritten by 12 344 "d": {61}, 345 } 346 n := map[string]simpleTest{ 347 "a": {16}, 348 "b": {}, 349 "c": {12}, 350 "e": {14}, 351 } 352 expect := map[string]simpleTest{ 353 "a": {16}, 354 "b": {}, 355 "c": {12}, 356 "d": {61}, 357 "e": {14}, 358 } 359 360 if err := MergeWithOverwrite(&m, n); err != nil { 361 t.Fatalf(err.Error()) 362 } 363 364 if !reflect.DeepEqual(m, expect) { 365 t.Fatalf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", m, expect) 366 } 367} 368 369func TestMaps(t *testing.T) { 370 m := map[string]simpleTest{ 371 "a": {}, 372 "b": {42}, 373 "c": {13}, 374 "d": {61}, 375 } 376 n := map[string]simpleTest{ 377 "a": {16}, 378 "b": {}, 379 "c": {12}, 380 "e": {14}, 381 } 382 expect := map[string]simpleTest{ 383 "a": {0}, 384 "b": {42}, 385 "c": {13}, 386 "d": {61}, 387 "e": {14}, 388 } 389 390 if err := Merge(&m, n); err != nil { 391 t.Fatalf(err.Error()) 392 } 393 394 if !reflect.DeepEqual(m, expect) { 395 t.Fatalf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", m, expect) 396 } 397 if m["a"].Value != 0 { 398 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) 399 } 400 if m["b"].Value != 42 { 401 t.Fatalf(`n wrongly merged in m: m["b"].Value(%d) != n["b"].Value(%d)`, m["b"].Value, n["b"].Value) 402 } 403 if m["c"].Value != 13 { 404 t.Fatalf(`n overwritten in m: m["c"].Value(%d) != n["c"].Value(%d)`, m["c"].Value, n["c"].Value) 405 } 406} 407 408func TestMapsWithNilPointer(t *testing.T) { 409 m := map[string]*simpleTest{ 410 "a": nil, 411 "b": nil, 412 } 413 n := map[string]*simpleTest{ 414 "b": nil, 415 "c": nil, 416 } 417 expect := map[string]*simpleTest{ 418 "a": nil, 419 "b": nil, 420 "c": nil, 421 } 422 423 if err := Merge(&m, n, WithOverride); err != nil { 424 t.Fatalf(err.Error()) 425 } 426 427 if !reflect.DeepEqual(m, expect) { 428 t.Fatalf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", m, expect) 429 } 430} 431 432func TestYAMLMaps(t *testing.T) { 433 thing := loadYAML("testdata/thing.yml") 434 license := loadYAML("testdata/license.yml") 435 ft := thing["fields"].(map[interface{}]interface{}) 436 fl := license["fields"].(map[interface{}]interface{}) 437 // license has one extra field (site) and another already existing in thing (author) that Mergo won't override. 438 expectedLength := len(ft) + len(fl) - 1 439 if err := Merge(&license, thing); err != nil { 440 t.Fatal(err.Error()) 441 } 442 currentLength := len(license["fields"].(map[interface{}]interface{})) 443 if currentLength != expectedLength { 444 t.Fatalf(`thing not merged in license properly, license must have %d elements instead of %d`, expectedLength, currentLength) 445 } 446 fields := license["fields"].(map[interface{}]interface{}) 447 if _, ok := fields["id"]; !ok { 448 t.Fatalf(`thing not merged in license properly, license must have a new id field from thing`) 449 } 450} 451 452func TestTwoPointerValues(t *testing.T) { 453 a := &simpleTest{} 454 b := &simpleTest{42} 455 if err := Merge(a, b); err != nil { 456 t.Fatalf(`Boom. You crossed the streams: %s`, err) 457 } 458} 459 460func TestMap(t *testing.T) { 461 a := complexTest{} 462 a.ID = "athing" 463 c := moreComplextText{a, simpleTest{}, simpleTest{}} 464 b := map[string]interface{}{ 465 "ct": map[string]interface{}{ 466 "st": map[string]interface{}{ 467 "value": 42, 468 }, 469 "sz": 1, 470 "id": "bthing", 471 }, 472 "st": &simpleTest{144}, // Mapping a reference 473 "zt": simpleTest{299}, // Mapping a missing field (zt doesn't exist) 474 "nt": simpleTest{3}, 475 } 476 if err := Map(&c, b); err != nil { 477 t.FailNow() 478 } 479 m := b["ct"].(map[string]interface{}) 480 n := m["st"].(map[string]interface{}) 481 o := b["st"].(*simpleTest) 482 p := b["nt"].(simpleTest) 483 if c.Ct.St.Value != 42 { 484 t.Fatalf("b not merged in properly: c.Ct.St.Value(%d) != b.Ct.St.Value(%d)", c.Ct.St.Value, n["value"]) 485 } 486 if c.St.Value != 144 { 487 t.Fatalf("b not merged in properly: c.St.Value(%d) != b.St.Value(%d)", c.St.Value, o.Value) 488 } 489 if c.Nt.Value != 3 { 490 t.Fatalf("b not merged in properly: c.Nt.Value(%d) != b.Nt.Value(%d)", c.St.Value, p.Value) 491 } 492 if c.Ct.sz == 1 { 493 t.Fatalf("a's private field sz not preserved from merge: c.Ct.sz(%d) == b.Ct.sz(%d)", c.Ct.sz, m["sz"]) 494 } 495 if c.Ct.ID == m["id"] { 496 t.Fatalf("a's field ID merged unexpectedly: c.Ct.ID(%s) == b.Ct.ID(%s)", c.Ct.ID, m["id"]) 497 } 498} 499 500func TestSimpleMap(t *testing.T) { 501 a := simpleTest{} 502 b := map[string]interface{}{ 503 "value": 42, 504 } 505 if err := Map(&a, b); err != nil { 506 t.FailNow() 507 } 508 if a.Value != 42 { 509 t.Fatalf("b not merged in properly: a.Value(%d) != b.Value(%v)", a.Value, b["value"]) 510 } 511} 512 513func TestIfcMap(t *testing.T) { 514 a := ifcTest{} 515 b := ifcTest{42} 516 if err := Map(&a, b); err != nil { 517 t.FailNow() 518 } 519 if a.I != 42 { 520 t.Fatalf("b not merged in properly: a.I(%d) != b.I(%d)", a.I, b.I) 521 } 522 if !reflect.DeepEqual(a, b) { 523 t.FailNow() 524 } 525} 526 527func TestIfcMapNoOverwrite(t *testing.T) { 528 a := ifcTest{13} 529 b := ifcTest{42} 530 if err := Map(&a, b); err != nil { 531 t.FailNow() 532 } 533 if a.I != 13 { 534 t.Fatalf("a not left alone: a.I(%d) == b.I(%d)", a.I, b.I) 535 } 536} 537 538func TestIfcMapWithOverwrite(t *testing.T) { 539 a := ifcTest{13} 540 b := ifcTest{42} 541 if err := MapWithOverwrite(&a, b); err != nil { 542 t.FailNow() 543 } 544 if a.I != 42 { 545 t.Fatalf("b not merged in properly: a.I(%d) != b.I(%d)", a.I, b.I) 546 } 547 if !reflect.DeepEqual(a, b) { 548 t.FailNow() 549 } 550} 551 552type pointerMapTest struct { 553 A int 554 hidden int 555 B *simpleTest 556} 557 558func TestBackAndForth(t *testing.T) { 559 pt := pointerMapTest{42, 1, &simpleTest{66}} 560 m := make(map[string]interface{}) 561 if err := Map(&m, pt); err != nil { 562 t.FailNow() 563 } 564 var ( 565 v interface{} 566 ok bool 567 ) 568 if v, ok = m["a"]; v.(int) != pt.A || !ok { 569 t.Fatalf("pt not merged in properly: m[`a`](%d) != pt.A(%d)", v, pt.A) 570 } 571 if v, ok = m["b"]; !ok { 572 t.Fatalf("pt not merged in properly: B is missing in m") 573 } 574 var st *simpleTest 575 if st = v.(*simpleTest); st.Value != 66 { 576 t.Fatalf("something went wrong while mapping pt on m, B wasn't copied") 577 } 578 bpt := pointerMapTest{} 579 if err := Map(&bpt, m); err != nil { 580 t.Fatal(err) 581 } 582 if bpt.A != pt.A { 583 t.Fatalf("pt not merged in properly: bpt.A(%d) != pt.A(%d)", bpt.A, pt.A) 584 } 585 if bpt.hidden == pt.hidden { 586 t.Fatalf("pt unexpectedly merged: bpt.hidden(%d) == pt.hidden(%d)", bpt.hidden, pt.hidden) 587 } 588 if bpt.B.Value != pt.B.Value { 589 t.Fatalf("pt not merged in properly: bpt.B.Value(%d) != pt.B.Value(%d)", bpt.B.Value, pt.B.Value) 590 } 591} 592 593func TestEmbeddedPointerUnpacking(t *testing.T) { 594 tests := []struct{ input pointerMapTest }{ 595 {pointerMapTest{42, 1, nil}}, 596 {pointerMapTest{42, 1, &simpleTest{66}}}, 597 } 598 newValue := 77 599 m := map[string]interface{}{ 600 "b": map[string]interface{}{ 601 "value": newValue, 602 }, 603 } 604 for _, test := range tests { 605 pt := test.input 606 if err := MapWithOverwrite(&pt, m); err != nil { 607 t.FailNow() 608 } 609 if pt.B.Value != newValue { 610 t.Fatalf("pt not mapped properly: pt.A.Value(%d) != m[`b`][`value`](%d)", pt.B.Value, newValue) 611 } 612 613 } 614} 615 616type structWithTimePointer struct { 617 Birth *time.Time 618} 619 620func TestTime(t *testing.T) { 621 now := time.Now() 622 dataStruct := structWithTimePointer{ 623 Birth: &now, 624 } 625 dataMap := map[string]interface{}{ 626 "Birth": &now, 627 } 628 b := structWithTimePointer{} 629 if err := Merge(&b, dataStruct); err != nil { 630 t.FailNow() 631 } 632 if b.Birth.IsZero() { 633 t.Fatalf("time.Time not merged in properly: b.Birth(%v) != dataStruct['Birth'](%v)", b.Birth, dataStruct.Birth) 634 } 635 if b.Birth != dataStruct.Birth { 636 t.Fatalf("time.Time not merged in properly: b.Birth(%v) != dataStruct['Birth'](%v)", b.Birth, dataStruct.Birth) 637 } 638 b = structWithTimePointer{} 639 if err := Map(&b, dataMap); err != nil { 640 t.FailNow() 641 } 642 if b.Birth.IsZero() { 643 t.Fatalf("time.Time not merged in properly: b.Birth(%v) != dataMap['Birth'](%v)", b.Birth, dataMap["Birth"]) 644 } 645} 646 647type simpleNested struct { 648 A int 649} 650 651type structWithNestedPtrValueMap struct { 652 NestedPtrValue map[string]*simpleNested 653} 654 655func TestNestedPtrValueInMap(t *testing.T) { 656 src := &structWithNestedPtrValueMap{ 657 NestedPtrValue: map[string]*simpleNested{ 658 "x": { 659 A: 1, 660 }, 661 }, 662 } 663 dst := &structWithNestedPtrValueMap{ 664 NestedPtrValue: map[string]*simpleNested{ 665 "x": {}, 666 }, 667 } 668 if err := Map(dst, src); err != nil { 669 t.FailNow() 670 } 671 if dst.NestedPtrValue["x"].A == 0 { 672 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) 673 } 674} 675 676func loadYAML(path string) (m map[string]interface{}) { 677 m = make(map[string]interface{}) 678 raw, _ := ioutil.ReadFile(path) 679 _ = yaml.Unmarshal(raw, &m) 680 return 681} 682 683type structWithMap struct { 684 m map[string]structWithUnexportedProperty 685} 686 687type structWithUnexportedProperty struct { 688 s string 689} 690 691func TestUnexportedProperty(t *testing.T) { 692 a := structWithMap{map[string]structWithUnexportedProperty{ 693 "key": {"hello"}, 694 }} 695 b := structWithMap{map[string]structWithUnexportedProperty{ 696 "key": {"hi"}, 697 }} 698 defer func() { 699 if r := recover(); r != nil { 700 t.Errorf("Should not have panicked") 701 } 702 }() 703 Merge(&a, b) 704} 705 706type structWithBoolPointer struct { 707 C *bool 708} 709 710func TestBooleanPointer(t *testing.T) { 711 bt, bf := true, false 712 src := structWithBoolPointer{ 713 &bt, 714 } 715 dst := structWithBoolPointer{ 716 &bf, 717 } 718 if err := Merge(&dst, src); err != nil { 719 t.FailNow() 720 } 721 if dst.C == src.C { 722 t.Fatalf("dst.C should be a different pointer than src.C") 723 } 724 if *dst.C != *src.C { 725 t.Fatalf("dst.C should be true") 726 } 727} 728