github.com/hedzr/evendeep@v0.4.8/ctrl_test.go (about) 1 package evendeep_test 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "fmt" 8 "io" 9 "math" 10 "reflect" 11 "strings" 12 "testing" 13 "time" 14 "unsafe" 15 16 "gopkg.in/hedzr/errors.v3" 17 18 "github.com/hedzr/evendeep" 19 "github.com/hedzr/evendeep/dbglog" 20 "github.com/hedzr/evendeep/diff" 21 "github.com/hedzr/evendeep/flags/cms" 22 "github.com/hedzr/evendeep/internal/tool" 23 "github.com/hedzr/evendeep/typ" 24 ) 25 26 const ( 27 helloString = "hello" 28 worldString = "world" 29 aHelloString = "Hello" 30 aWorldString = "World" 31 ) 32 33 func TestDeepCopyForInvalidSourceOrTarget(t *testing.T) { 34 invalidObj := func() typ.Any { 35 var x *evendeep.X0 36 return x 37 } 38 t.Run("invalid source", func(t *testing.T) { 39 src := invalidObj() 40 tgt := invalidObj() 41 evendeep.DeepCopy(src, &tgt) 42 t.Logf("tgt: %+v", tgt) 43 }) 44 t.Run("valid ptr to invalid source", func(t *testing.T) { 45 src := invalidObj() 46 tgt := invalidObj() 47 evendeep.DeepCopy(&src, &tgt) 48 t.Logf("tgt: %+v", tgt) 49 }) 50 51 nilmap := func() typ.Any { 52 var mm []map[string]struct{} 53 return mm 54 } 55 t.Run("nil map", func(t *testing.T) { 56 src := nilmap() 57 tgt := nilmap() 58 evendeep.DeepCopy(src, &tgt) 59 t.Logf("tgt: %+v", tgt) 60 }) 61 t.Run("valid ptr to nil map", func(t *testing.T) { 62 src := nilmap() 63 tgt := nilmap() 64 evendeep.DeepCopy(&src, &tgt) 65 t.Logf("tgt: %+v", tgt) 66 }) 67 68 nilslice := func() typ.Any { 69 var mm []map[string]struct{} 70 return mm 71 } 72 t.Run("nil slice", func(t *testing.T) { 73 src := nilslice() 74 tgt := nilslice() 75 evendeep.DeepCopy(src, &tgt) 76 t.Logf("tgt: %+v", tgt) 77 }) 78 t.Run("valid ptr to nil slice", func(t *testing.T) { 79 src := nilslice() 80 tgt := nilslice() 81 evendeep.DeepCopy(&src, &tgt) 82 t.Logf("tgt: %+v", tgt) 83 }) 84 } 85 86 type ccs struct { 87 string 88 int 89 *float64 90 } 91 92 func (s *ccs) Clone() interface{} { 93 return &ccs{ 94 string: s.string, 95 int: s.int, 96 float64: &(*s.float64), // nolint:staticcheck 97 } 98 } 99 100 func TestCloneableSource(t *testing.T) { 101 cloneable := func() *ccs { 102 f := evendeep.Randtool.NextFloat64() 103 return &ccs{ 104 string: evendeep.Randtool.NextStringSimple(13), 105 int: evendeep.Randtool.NextIn(300), 106 float64: &f, 107 } 108 } 109 110 t.Run("invoke Cloneable interface", func(t *testing.T) { 111 src := cloneable() 112 tgt := cloneable() 113 sav := *tgt 114 evendeep.DeepCopy(&src, &tgt) 115 t.Logf("src: %v, old: %v, new tgt: %v", src, sav, tgt) 116 if reflect.DeepEqual(src, tgt) == false { 117 var err error 118 dif, equal := diff.New(src, tgt) 119 if !equal { 120 fmt.Println(dif) 121 err = errors.New("diff.PrettyDiff identified its not equal:\ndifferent:\n%v", dif) 122 } 123 t.Fatalf("not equal. %v", err) 124 } 125 }) 126 } 127 128 type dcs struct { 129 string 130 int 131 *float64 132 } 133 134 func (s *dcs) DeepCopy() interface{} { 135 return &dcs{ 136 string: s.string, 137 int: s.int, 138 float64: &(*s.float64), // nolint:staticcheck 139 } 140 } 141 142 func TestDeepCopyableSource(t *testing.T) { 143 copyable := func() *dcs { 144 f := evendeep.Randtool.NextFloat64() 145 return &dcs{ 146 string: evendeep.Randtool.NextStringSimple(13), 147 int: evendeep.Randtool.NextIn(300), 148 float64: &f, 149 } 150 } 151 152 t.Run("invoke DeepCopyable interface", func(t *testing.T) { 153 src := copyable() 154 tgt := copyable() 155 sav := *tgt 156 evendeep.DeepCopy(&src, &tgt) 157 t.Logf("src: %v, old: %v, new tgt: %v", src, sav, tgt) 158 if reflect.DeepEqual(src, tgt) == false { 159 var err error 160 dif, equal := diff.New(src, tgt) 161 if !equal { 162 fmt.Println(dif) 163 err = errors.New("diff.PrettyDiff identified its not equal:\ndifferent:\n%v", dif) 164 } 165 t.Fatalf("not equal. %v", err) 166 } 167 }) // NewTasskks creates a 168 } 169 170 func TestSimple(t *testing.T) { 171 172 // var dInt = 9 173 // var dStr = worldString 174 175 for _, tc := range []evendeep.TestCase{ 176 evendeep.NewTestCase( 177 "primitive - int", 178 8, 9, 8, 179 nil, 180 nil, 181 ), 182 evendeep.NewTestCase( 183 "primitive - string", 184 helloString, worldString, helloString, 185 []evendeep.Opt{ 186 evendeep.WithStrategiesReset(cms.Default), 187 }, 188 nil, 189 ), 190 evendeep.NewTestCase( 191 "primitive - string slice", 192 []string{helloString, worldString}, 193 &[]string{"andy"}, // target needn't addressof 194 &[]string{helloString, worldString}, // SliceCopy: copy to target; SliceCopyAppend: append to target; SliceMerge: merge into slice 195 []evendeep.Opt{ 196 evendeep.WithStrategiesReset(), 197 }, 198 nil, 199 ), 200 evendeep.NewTestCase( 201 "primitive - string slice - merge", 202 []string{helloString, helloString, worldString}, // elements in source will be merged into target with uniqueness. 203 &[]string{"andy", "andy"}, // target needn't addressof 204 &[]string{"andy", helloString, worldString}, // In merge mode, any dup elems will be removed. 205 []evendeep.Opt{ 206 evendeep.WithMergeStrategyOpt, 207 }, 208 nil, 209 ), 210 evendeep.NewTestCase( 211 "primitive - int slice", 212 []int{7, 99}, 213 &[]int{5}, 214 &[]int{7, 99}, 215 []evendeep.Opt{ 216 evendeep.WithStrategiesReset(), 217 }, 218 nil, 219 ), 220 evendeep.NewTestCase( 221 "primitive - int slice - merge", 222 []int{7, 99}, 223 &[]int{5}, 224 &[]int{5, 7, 99}, 225 []evendeep.Opt{ 226 evendeep.WithStrategies(cms.SliceMerge), 227 }, 228 nil, 229 ), 230 evendeep.NewTestCase( 231 "primitive types - int slice - merge for dup", 232 []int{99, 7}, &[]int{125, 99}, &[]int{125, 99, 7}, 233 []evendeep.Opt{ 234 evendeep.WithStrategies(cms.SliceMerge), 235 }, 236 nil, 237 ), 238 // NEED REVIEW: what is copyenh strategy 239 // evendeep.NewTestCase( 240 // "primitive types - int slice - copyenh(overwrite and extend)", 241 // []int{13, 7, 99}, []int{125, 99}, []int{7, 99, 7}, 242 // []evendeep.Opt{ 243 // evendeep.WithStrategies(evendeep.SliceCopyOverwrite), 244 // }, 245 // nil, 246 // ), 247 } { 248 t.Run(evendeep.RunTestCasesWith(&tc)) // nolint:gosec // G601: Implicit memory aliasing in for loop 249 } 250 251 } 252 253 func TestTypeConvert(t *testing.T) { 254 255 var i9 = 9 256 var i5 = 5 257 var ui6 = uint(6) 258 var i64 int64 = 10 259 var f64 = 9.1 260 261 cases := []evendeep.TestCase{ 262 evendeep.NewTestCase( 263 "int -> int64", 264 8, i64, int64(8), 265 nil, 266 nil, 267 ), 268 evendeep.NewTestCase( 269 "int64 -> int", 270 int64(8), i5, 8, 271 nil, 272 nil, 273 ), 274 evendeep.NewTestCase( 275 "int64 -> uint", 276 int64(8), ui6, uint(8), 277 nil, 278 nil, 279 ), 280 evendeep.NewTestCase( 281 "float32 -> float64", 282 float32(8.1), f64, float64(8.100000381469727), 283 nil, 284 nil, 285 ), 286 evendeep.NewTestCase( 287 "complex -> complex128", 288 complex64(8.1+3i), complex128(9.1), complex128(8.100000381469727+3i), 289 nil, 290 nil, 291 ), 292 evendeep.NewTestCase( 293 "complex -> int - ErrCannotConvertTo test", 294 complex64(8.1+3i), &i5, int(8), 295 nil, 296 func(src, dst, expect typ.Any, e error) (err error) { 297 if errors.IsDescended(evendeep.ErrCannotConvertTo, e) { 298 return 299 } 300 return e 301 }, 302 ), 303 evendeep.NewTestCase( 304 "int -> intptr", 305 8, &i9, 8, 306 nil, 307 func(src, dst, expect typ.Any, e error) (err error) { 308 if d, ok := dst.(*int); ok && e == nil { 309 if *d == src { 310 return 311 } 312 } 313 return errors.DataLoss 314 }, 315 ), 316 } 317 318 for ix, tc := range cases { 319 if !t.Run(fmt.Sprintf("%3d. %s", ix, tc.Description), evendeep.DefaultDeepCopyTestRunner(ix, tc)) { 320 break 321 } 322 } 323 } 324 325 func TestTypeConvert2Slice(t *testing.T) { 326 327 var i9 = 9 328 var i5 = 5 329 // var ui6 = uint(6) 330 // var i64 int64 = 10 331 // var f64 float64 = 9.1 332 333 // slice 334 335 var si64 = []int64{9} 336 var si = []int{9} 337 var sui = []uint{9} 338 var sf64 = []float64{9.1} 339 var sc128 = []complex128{9.1} 340 341 opts := []evendeep.Opt{ 342 evendeep.WithStrategies(cms.SliceMerge), 343 } 344 345 cases := []evendeep.TestCase{ 346 evendeep.NewTestCase( 347 "[]int -> []int64", 348 []int{8}, &si64, &[]int64{9, 8}, 349 opts, 350 nil, 351 ), 352 evendeep.NewTestCase( 353 "int -> []int64", 354 7, &si64, &[]int64{9, 8, 7}, 355 opts, 356 nil, 357 ), 358 evendeep.NewTestCase( 359 "[]int64 -> []int", 360 []int64{8}, &si, &[]int{9, 8}, 361 opts, 362 nil, 363 ), 364 evendeep.NewTestCase( 365 "int64 -> []int", 366 int64(7), &si, &[]int{9, 8, 7}, 367 opts, 368 nil, 369 ), 370 evendeep.NewTestCase( 371 "[]int64 -> []int (truncate the overflowed input)", 372 []int64{math.MaxInt64}, &si, &[]int{9, 8, 7, cms.MaxInt}, 373 opts, 374 nil, 375 ), 376 evendeep.NewTestCase( 377 "int64 -> []uint", 378 int64(8), sui, []uint{9, 8}, 379 opts, 380 nil, 381 ), 382 evendeep.NewTestCase( 383 "int64 -> *[]uint", 384 int64(8), &sui, &[]uint{9, 8}, 385 opts, 386 nil, 387 ), 388 evendeep.NewTestCase( 389 "float32 -> []float64", 390 float32(8.1), &sf64, &[]float64{9.1, 8.100000381469727}, 391 opts, 392 nil, 393 ), 394 evendeep.NewTestCase( 395 "[]float32 -> []float64", 396 []float32{8.1}, &sf64, &[]float64{9.1, 8.100000381469727}, 397 opts, 398 nil, 399 ), 400 evendeep.NewTestCase( 401 "complex64 -> []complex128", 402 complex64(8.1+3i), &sc128, &[]complex128{9.1, 8.100000381469727 + 3i}, 403 opts, 404 nil, 405 ), 406 evendeep.NewTestCase( 407 "[]complex64 -> []complex128", 408 []complex64{8.1 + 3i}, &sc128, &[]complex128{9.1 + 0i, 8.100000381469727 + 3i}, 409 opts, 410 nil, 411 ), 412 evendeep.NewTestCase( 413 "complex -> int - ErrCannotConvertTo test", 414 complex64(8.1+3i), &i5, int(8), 415 opts, 416 func(src, dst, expect typ.Any, e error) (err error) { 417 if errors.IsDescended(evendeep.ErrCannotConvertTo, e) { 418 return 419 } 420 return e 421 }, 422 ), 423 evendeep.NewTestCase( 424 "int -> intptr", 425 8, &i9, 8, 426 opts, 427 func(src, dst, expect typ.Any, e error) (err error) { 428 if d, ok := dst.(*int); ok && e == nil { 429 if *d == src { 430 return 431 } 432 } 433 return errors.DataLoss 434 }, 435 ), 436 } 437 438 for ix, tc := range cases { 439 if !t.Run(fmt.Sprintf("%3d. %s", ix, tc.Description), evendeep.DefaultDeepCopyTestRunner(ix, tc)) { 440 break 441 } 442 } 443 } 444 445 func TestTypeConvert3Func(t *testing.T) { 446 // type B struct { 447 // F func(int) (int, error) 448 // } 449 // b1 := B{F: func(i int) (int, error) { i1 = i * 2; return i1, nil }} 450 451 opts := []evendeep.Opt{ 452 evendeep.WithPassSourceToTargetFunctionOpt, 453 } 454 455 i1 := 0 456 b1 := func(i []int) (int, error) { i1 = i[0] * 2; return i1, nil } 457 // var e1 error 458 b2 := func(i int) (int, error) { 459 if i > 0 { 460 return 0, errors.BadRequest 461 } 462 return i, nil 463 } 464 465 cases := []evendeep.TestCase{ 466 evendeep.NewTestCase( 467 "[]int -> func(int)(int,error)", 468 []int{8}, &b1, nil, 469 opts, 470 func(src, dst, expect typ.Any, e error) (err error) { 471 if i1 != 16 { 472 err = errors.BadRequest 473 } 474 return 475 }, 476 ), 477 evendeep.NewTestCase( 478 "int -> func(int)(int,error)", 479 8, &b2, nil, 480 opts, 481 func(src, dst, expect typ.Any, e error) (err error) { 482 if !errors.Is(e, errors.BadRequest) { 483 err = errors.BadRequest 484 } 485 return 486 }, 487 ), 488 } 489 490 for ix, tc := range cases { 491 if !t.Run(fmt.Sprintf("%3d. %s", ix, tc.Description), evendeep.DefaultDeepCopyTestRunner(ix, tc)) { 492 break 493 } 494 } 495 } 496 497 func TestErrorCodeIs(t *testing.T) { 498 var err error = errors.BadRequest 499 if !errors.Is(err, errors.BadRequest) { 500 t.Fatalf("want is") 501 } 502 err = io.ErrClosedPipe 503 if errors.Is(err, errors.BadRequest) { 504 t.Fatalf("want not is") 505 } 506 err = errors.NotFound 507 if errors.Is(err, errors.BadRequest) { 508 t.Fatalf("want not is (code)") 509 } 510 } 511 512 func TestStructStdlib(t *testing.T) { 513 514 // timeZone, _ := time.LoadLocation("America/Phoenix") 515 timeZone2, _ := time.LoadLocation("Asia/Chongqing") 516 tm1 := time.Date(1979, 1, 29, 13, 3, 49, 19730313, timeZone2) 517 var tgt time.Time 518 var dur time.Duration 519 var dur1 = 13*time.Second + 3*time.Nanosecond 520 var bb, bb1 bytes.Buffer 521 bb1.WriteString("hellp world") 522 var b, be []byte 523 be = bb1.Bytes() 524 525 var bbn *bytes.Buffer = nil 526 527 for _, tc := range []evendeep.TestCase{ 528 evendeep.NewTestCase( 529 "stdlib - time.Time 1", 530 tm1, &tgt, &tm1, 531 nil, 532 nil, 533 ), 534 evendeep.NewTestCase( 535 "stdlib - time.Duration 1", 536 dur1, &dur, &dur1, 537 nil, 538 nil, 539 ), 540 evendeep.NewTestCase( 541 "stdlib - bytes.Buffer 1", 542 bb1, &bb, &bb1, 543 nil, 544 nil, 545 ), 546 evendeep.NewTestCase( 547 "stdlib - bytes.Buffer 2", 548 bb1, &b, &be, 549 nil, 550 nil, 551 ), 552 evendeep.NewTestCase( 553 "stdlib - bytes.Buffer 2 - target is nil", 554 bb1, &bbn, &bb1, 555 nil, 556 func(src, dst, expect typ.Any, e error) (err error) { 557 if err = e; e != nil { 558 return 559 } 560 if p, ok := dst.(**bytes.Buffer); ok && *p == nil { 561 return 562 } else { 563 dbglog.Log("p = %v, ok = %v, dst = %v/%v", p, ok, dst, &bbn) 564 } 565 err = errors.InvalidArgument 566 return 567 }, 568 ), 569 } { 570 t.Run(evendeep.RunTestCasesWith(&tc)) // nolint:gosec // G601: Implicit memory aliasing in for loop 571 } 572 573 } 574 575 func TestStructSimple(t *testing.T) { 576 // defer dbglog.NewCaptureLog(t).Release() 577 578 nn := []int{2, 9, 77, 111, 23, 29} 579 var a [2]string 580 a[0] = aHelloString 581 a[1] = aWorldString 582 var a3 = [3]string{aHelloString, aWorldString} 583 584 x0 := evendeep.X0{} 585 x1 := evendeep.X1{ 586 A: uintptr(unsafe.Pointer(&x0)), 587 H: make(chan int, 5), 588 M: unsafe.Pointer(&x0), 589 // E: []*X0{&x0}, 590 N: nn[1:5], 591 O: a, 592 Q: a, 593 } 594 595 expect1 := &evendeep.X2{ 596 A: uintptr(unsafe.Pointer(&x0)), 597 // D: []string{}, 598 // E: []*evendeep.X0{}, 599 H: make(chan int, 5), 600 K: &x0, 601 M: unsafe.Pointer(&x0), 602 // E: []*X0{&x0}, 603 N: nn[1:5], 604 O: a, 605 Q: a3, 606 } 607 x2 := evendeep.X2{N: []int{23, 8}} 608 expect2 := &evendeep.X2{ 609 A: uintptr(unsafe.Pointer(&x0)), 610 H: x1.H, 611 K: &x0, 612 M: unsafe.Pointer(&x0), 613 // E: []*X0{&x0}, 614 N: []int{23, 8, 9, 77, 111}, // Note: [23,8] + [9,77,111,23] -> [23,8,9,77,111] 615 O: a, 616 Q: a3, 617 } 618 t.Logf("expect.Q: %v", expect1.Q) 619 620 t.Logf(" src: %+v", x1) 621 t.Logf(" tgt: %+v", evendeep.X2{N: nn[1:3]}) 622 623 cases := []evendeep.TestCase{ 624 evendeep.NewTestCase( 625 "struct - 1", 626 x1, &evendeep.X2{N: nn[1:3]}, expect1, 627 []evendeep.Opt{ 628 evendeep.WithStrategiesReset(), 629 // evendeep.WithStrategies(cms.OmitIfEmpty), 630 evendeep.WithAutoNewForStructFieldOpt, 631 }, 632 nil, 633 // func(src, dst, expect typ.Any) (err error) { 634 // dif, equal := diff.New(expect, dst) 635 // if !equal { 636 // fmt.Println(dif) 637 // } 638 // return 639 // }, 640 ), 641 evendeep.NewTestCase( 642 "struct - 2 - merge", 643 x1, &x2, 644 expect2, 645 []evendeep.Opt{ 646 evendeep.WithStrategies(cms.SliceMerge), 647 evendeep.WithAutoNewForStructFieldOpt, 648 }, 649 nil, 650 ), 651 } 652 653 for ix, tc := range cases { 654 if !t.Run(fmt.Sprintf("%3d. %s", ix, tc.Description), evendeep.DefaultDeepCopyTestRunner(ix, tc)) { 655 break 656 } 657 } 658 } 659 660 func TestStructEmbedded(t *testing.T) { 661 662 timeZone, _ := time.LoadLocation("America/Phoenix") 663 tm := time.Date(1999, 3, 13, 5, 57, 11, 1901, timeZone) 664 tm2 := time.Date(2003, 9, 1, 23, 59, 59, 3579, timeZone) 665 666 src := evendeep.Employee2{ 667 Base: evendeep.Base{ 668 Name: "Bob", 669 Birthday: &tm, 670 Age: 24, 671 EmployeID: 7, 672 }, 673 Avatar: "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet&rs=1", 674 Image: []byte{95, 27, 43, 66, 0, 21, 210}, 675 Attr: &evendeep.Attr{Attrs: []string{helloString, worldString}}, 676 Valid: true, 677 } 678 679 tgt := evendeep.User{ 680 Name: "Frank", 681 Birthday: &tm2, 682 Age: 18, 683 EmployeID: 9, 684 Attr: &evendeep.Attr{Attrs: []string{"baby"}}, 685 Deleted: true, 686 } 687 688 expect1 := &evendeep.User{ 689 Name: "Bob", 690 Birthday: &tm, 691 Age: 24, 692 EmployeID: 7, 693 Avatar: "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet&rs=1", 694 Image: []byte{95, 27, 43, 66, 0, 21, 210}, 695 Attr: &evendeep.Attr{Attrs: []string{"baby", helloString, worldString}}, 696 Valid: true, 697 } 698 699 cases := []evendeep.TestCase{ 700 evendeep.NewTestCase( 701 "struct - 1", 702 src, &tgt, 703 expect1, 704 []evendeep.Opt{ 705 evendeep.WithMergeStrategyOpt, 706 evendeep.WithAutoExpandStructOpt, 707 }, 708 nil, 709 // func(src, dst, expect typ.Any) (err error) { 710 // dif, equal := diff.New(expect, dst) 711 // if !equal { 712 // fmt.Println(dif) 713 // } 714 // return 715 // }, 716 ), 717 } 718 719 for ix, tc := range cases { 720 if !t.Run(fmt.Sprintf("%3d. %s", ix, tc.Description), evendeep.DefaultDeepCopyTestRunner(ix, tc)) { 721 break 722 } 723 } 724 } 725 726 func TestStructToSliceOrMap(t *testing.T) { 727 728 timeZone, _ := time.LoadLocation("America/Phoenix") 729 tm := time.Date(1999, 3, 13, 5, 57, 11, 1901, timeZone) 730 // timeZone2, _ := time.LoadLocation("Asia/Chongqing") 731 // tm1 := time.Date(2021, 2, 28, 13, 1, 23, 800, timeZone2) 732 // tm2 := time.Date(2003, 9, 1, 23, 59, 59, 3579, timeZone) 733 // tm3 := time.Date(2015, 1, 29, 19, 31, 37, 77, timeZone2) 734 735 src := evendeep.Employee2{ 736 Base: evendeep.Base{ 737 Name: "Bob", 738 Birthday: &tm, 739 Age: 24, 740 EmployeID: 7, 741 }, 742 Avatar: "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet&rs=1", 743 Image: []byte{95, 27, 43, 66, 0, 21, 210}, 744 Attr: &evendeep.Attr{Attrs: []string{helloString, worldString}}, 745 Valid: true, 746 } 747 748 var slice1 []evendeep.User 749 var slice2 []*evendeep.User 750 751 var map1 = make(map[string]typ.Any) 752 753 expect1 := evendeep.User{ 754 Name: "Bob", 755 Birthday: &tm, 756 Age: 24, 757 EmployeID: 7, 758 Avatar: "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet&rs=1", 759 Image: []byte{95, 27, 43, 66, 0, 21, 210}, 760 Attr: &evendeep.Attr{Attrs: []string{helloString, worldString}}, 761 Valid: true, 762 } 763 764 expect3 := map[string]typ.Any{ 765 "Name": "Bob", 766 "Birthday": tm, 767 "Age": 24, 768 "EmployeID": int64(7), 769 "Avatar": "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet&rs=1", 770 "Image": []byte{95, 27, 43, 66, 0, 21, 210}, 771 "Attrs": []string{helloString, worldString}, 772 "Valid": true, 773 "Deleted": false, 774 } 775 776 t.Run("struct - slice - 1", func(t *testing.T) { 777 // 778 }) 779 780 var str string 781 expectJSON := `{ 782 "Name": "Bob", 783 "Birthday": "1999-03-13T05:57:11.000001901-07:00", 784 "Age": 24, 785 "EmployeID": 7, 786 "Avatar": "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet\u0026rs=1", 787 "Image": "XxsrQgAV0g==", 788 "Attr": { 789 "Attrs": [ 790 "hello", 791 "world" 792 ] 793 }, 794 "Valid": true, 795 "Deleted": false 796 }` 797 798 cases := []evendeep.TestCase{ 799 evendeep.NewTestCase( 800 "struct -> slice []obj", 801 src, &slice1, &[]evendeep.User{expect1}, 802 []evendeep.Opt{evendeep.WithMergeStrategyOpt, evendeep.WithAutoExpandStructOpt, evendeep.WithAutoNewForStructFieldOpt}, 803 nil, 804 ), 805 806 evendeep.NewTestCase( 807 "struct -> string", 808 src, &str, &expectJSON, 809 []evendeep.Opt{ 810 evendeep.WithStringMarshaller(func(v interface{}) ([]byte, error) { 811 return json.MarshalIndent(v, "", " ") 812 }), 813 evendeep.WithMergeStrategyOpt, 814 evendeep.WithAutoExpandStructOpt, 815 evendeep.WithAutoNewForStructFieldOpt}, 816 nil, 817 ), 818 819 evendeep.NewTestCase( 820 "struct -> map[string]Any", 821 src, &map1, &expect3, 822 []evendeep.Opt{evendeep.WithMergeStrategyOpt, evendeep.WithAutoExpandStructOpt, evendeep.WithAutoNewForStructFieldOpt}, 823 nil, 824 ), 825 826 evendeep.NewTestCase( 827 "struct -> slice []obj", 828 src, &slice1, &[]evendeep.User{expect1}, 829 []evendeep.Opt{evendeep.WithMergeStrategyOpt, evendeep.WithAutoExpandStructOpt, evendeep.WithAutoNewForStructFieldOpt}, 830 nil, 831 ), 832 evendeep.NewTestCase( 833 "struct -> slice []*obj", 834 src, &slice2, &[]*evendeep.User{&expect1}, 835 []evendeep.Opt{evendeep.WithMergeStrategyOpt, evendeep.WithAutoExpandStructOpt, evendeep.WithAutoNewForStructFieldOpt}, 836 nil, 837 ), 838 } 839 840 for ix, tc := range cases { 841 if !t.Run(fmt.Sprintf("%3d. %s", ix, tc.Description), evendeep.DefaultDeepCopyTestRunner(ix, tc)) { 842 break 843 } 844 } 845 } 846 847 func TestStructWithSourceExtractor(t *testing.T) { 848 type MyValue map[string]typ.Any 849 type MyKey string 850 const key MyKey = "data-in-sess" 851 c := context.WithValue(context.TODO(), key, MyValue{ 852 "A": 12, 853 }) 854 855 tgt := struct { 856 A int 857 }{} 858 859 err := evendeep.New().CopyTo(c, &tgt, 860 evendeep.WithSourceValueExtractor(func(targetName string) typ.Any { 861 if m, ok := c.Value(key).(MyValue); ok { 862 return m[targetName] 863 } 864 return nil 865 }), 866 ) 867 868 if tgt.A != 12 || err != nil { 869 t.Fatalf(`err: %+v`, err) 870 } 871 } 872 873 func TestStructWithTargetSetter_struct2struct(t *testing.T) { 874 type srcS struct { 875 A int64 876 B bool 877 C string 878 D float64 879 } 880 type dstS struct { 881 MoA int32 882 MoB bool 883 MoC string 884 MoZ string 885 } 886 src := &srcS{ 887 A: 5, 888 B: true, 889 C: helloString, 890 } 891 tgt := &dstS{ 892 MoA: 1, 893 MoB: false, 894 MoZ: worldString, 895 } 896 897 setStructByName := func(s reflect.Value, fld string, val reflect.Value) { 898 var f = s.FieldByName(fld) 899 if f.IsValid() { 900 if val.Type().ConvertibleTo(f.Type()) { 901 f.Set(val.Convert(f.Type())) 902 } else { 903 f.Set(val) 904 } 905 } 906 } 907 err := evendeep.New().CopyTo(src, &tgt, 908 evendeep.WithTargetValueSetter(func(value *reflect.Value, sourceNames ...string) (err error) { 909 if value != nil { 910 name := "Mo" + strings.Join(sourceNames, ".") 911 setStructByName(reflect.ValueOf(tgt).Elem(), name, *value) 912 } 913 return // ErrShouldFallback to call the evendeep standard processing 914 }), 915 ) 916 917 if err != nil || tgt.MoA != 5 || !tgt.MoB || tgt.MoC != helloString || tgt.MoZ != worldString { 918 t.Errorf("err: %v, tgt: %v", err, tgt) 919 t.FailNow() 920 } else { 921 t.Logf("new map got: %v", tgt) 922 } 923 } 924 925 func TestStructWithTargetSetter_struct2map(t *testing.T) { 926 type srcS struct { 927 A int 928 B bool 929 C string 930 } 931 932 src := &srcS{ 933 A: 5, 934 B: true, 935 C: helloString, 936 } 937 tgt := map[string]typ.Any{ 938 "Z": worldString, 939 } 940 941 err := evendeep.New().CopyTo(src, &tgt, 942 evendeep.WithTargetValueSetter(func(value *reflect.Value, sourceNames ...string) (err error) { 943 if value != nil { 944 name := "Mo" + strings.Join(sourceNames, ".") 945 tgt[name] = value.Interface() 946 } 947 return // ErrShouldFallback to call the evendeep standard processing 948 }), 949 ) 950 951 if err != nil || tgt["MoA"] != 5 || tgt["MoB"] != true || tgt["MoC"] != helloString || tgt["Z"] != worldString { 952 t.Errorf("err: %v, tgt: %v", err, tgt) 953 t.FailNow() 954 } else if _, ok := tgt["A"]; ok { 955 t.Errorf("err: key 'A' shouldn't exists, tgt: %v", tgt) 956 t.FailNow() 957 } else { 958 t.Logf("new map got: %v", tgt) 959 } 960 } 961 962 func TestStructWithTargetSetter_map2struct(t *testing.T) { 963 type dstS struct { 964 MoA int32 965 MoB bool 966 MoC string 967 MoZ string 968 } 969 src := map[string]typ.Any{ 970 "A": 5, 971 "B": true, 972 "C": helloString, 973 } 974 tgt := &dstS{ 975 MoA: 1, 976 MoB: false, 977 MoZ: worldString, 978 } 979 980 setStructByName := func(s reflect.Value, fldName string, value reflect.Value) { 981 var f = s.FieldByName(fldName) 982 if f.IsValid() { 983 if value.Type().ConvertibleTo(f.Type()) { 984 dbglog.Log("struct.%q <- %v", fldName, tool.Valfmt(&value)) 985 f.Set(value.Convert(f.Type())) 986 } else { 987 dbglog.Log("struct.%q <- %v", fldName, tool.Valfmt(&value)) 988 f.Set(value) 989 } 990 } 991 } 992 err := evendeep.New().CopyTo(src, &tgt, 993 evendeep.WithTargetValueSetter(func(value *reflect.Value, sourceNames ...string) (err error) { 994 if value != nil { 995 name := "Mo" + strings.Join(sourceNames, ".") 996 setStructByName(reflect.ValueOf(tgt).Elem(), name, *value) 997 dbglog.Log("struct.%q <- %v", name, tool.Valfmt(value)) 998 } 999 return // ErrShouldFallback to call the evendeep standard processing 1000 }), 1001 ) 1002 1003 if err != nil || tgt.MoA != 5 || !tgt.MoB || tgt.MoC != helloString || tgt.MoZ != worldString { 1004 t.Errorf("err: %v, tgt: %v", err, tgt) 1005 t.FailNow() 1006 } else { 1007 t.Logf("new map got: %v", tgt) 1008 } 1009 } 1010 1011 func TestStructWithTargetSetter_map2map(t *testing.T) { 1012 src := map[string]typ.Any{ 1013 "A": 5, 1014 "B": true, 1015 "C": helloString, 1016 } 1017 tgt := map[string]typ.Any{ 1018 "Z": worldString, 1019 } 1020 1021 err := evendeep.New().CopyTo(src, &tgt, 1022 evendeep.WithTargetValueSetter(func(value *reflect.Value, sourceNames ...string) (err error) { 1023 if value != nil { 1024 name := "Mo" + strings.Join(sourceNames, ".") 1025 tgt[name] = value.Interface() 1026 } 1027 return // ErrShouldFallback to call the evendeep standard processing 1028 }), 1029 ) 1030 1031 if err != nil || tgt["MoA"] != 5 || tgt["MoB"] != true || tgt["MoC"] != helloString || tgt["Z"] != worldString { 1032 t.Errorf("err: %v, tgt: %v", err, tgt) 1033 t.FailNow() 1034 } else if _, ok := tgt["A"]; ok { 1035 t.Errorf("err: key 'A' shouldn't exists, tgt: %v", tgt) 1036 t.FailNow() 1037 } else { 1038 t.Logf("new map got: %v", tgt) 1039 } 1040 } 1041 1042 func TestStructWithSSS(t *testing.T) { 1043 // 1044 } 1045 1046 type aS struct { 1047 A int 1048 b bool 1049 C string 1050 } 1051 1052 func (s aS) B() bool { return s.b } 1053 1054 func TestStructWithCmsByNameStrategy(t *testing.T) { 1055 type bS struct { 1056 Z int 1057 B bool 1058 C string 1059 } 1060 1061 src := &aS{A: 6, b: true, C: helloString} 1062 var tgt = bS{Z: 1} 1063 1064 // use ByName strategy, 1065 // use copyFunctionResultsToTarget 1066 err := evendeep.New().CopyTo(src, &tgt, evendeep.WithByNameStrategyOpt) 1067 1068 if tgt.Z != 1 || !tgt.B || tgt.C != helloString || err != nil { 1069 t.Fatalf("BAD COPY, tgt: %+v", tgt) 1070 } 1071 } 1072 1073 func TestStructWithNameConversions(t *testing.T) { 1074 type srcS struct { 1075 A int `copy:"A1"` 1076 B bool `copy:"B1,std"` 1077 C string `copy:"C1,"` 1078 } 1079 1080 type dstS struct { 1081 A1 int 1082 B1 bool 1083 C1 string 1084 } 1085 1086 src := &srcS{A: 6, B: true, C: helloString} 1087 var tgt = dstS{A1: 1} 1088 1089 // use ByName strategy, 1090 err := evendeep.New().CopyTo(src, &tgt, evendeep.WithByNameStrategyOpt) 1091 1092 if tgt.A1 != 6 || !tgt.B1 || tgt.C1 != helloString || err != nil { 1093 t.Fatalf("BAD COPY, tgt: %+v", tgt) 1094 } 1095 } 1096 1097 func TestStructWithNameConverter(t *testing.T) { 1098 // TODO enable name converter for each field, and TestStructWithNameConverter() 1099 } 1100 1101 func TestSliceSimple(t *testing.T) { 1102 1103 tgt := []float32{3.1, 4.5, 9.67} 1104 itgt := []int{13, 5} 1105 1106 cases := []evendeep.TestCase{ 1107 evendeep.NewTestCase( 1108 "slice (float64 -> float32)", 1109 []float64{9.123, 5.2}, &tgt, &[]float32{3.1, 4.5, 9.67, 9.123, 5.2}, 1110 []evendeep.Opt{evendeep.WithMergeStrategyOpt}, 1111 nil, 1112 ), 1113 evendeep.NewTestCase( 1114 "slice (uint64 -> int)", 1115 []uint64{9, 5}, &itgt, &[]int{13, 5, 9}, 1116 []evendeep.Opt{evendeep.WithMergeStrategyOpt}, 1117 nil, 1118 ), 1119 } 1120 1121 for ix, tc := range cases { 1122 if !t.Run(fmt.Sprintf("%3d. %s", ix, tc.Description), evendeep.DefaultDeepCopyTestRunner(ix, tc)) { 1123 break 1124 } 1125 } 1126 } 1127 1128 func TestSliceTypeConvert(t *testing.T) { 1129 1130 // tgt := []float32{3.1, 4.5, 9.67} 1131 // itgt := []int{13, 5} 1132 stgt := []string{"-", "2.718280076980591"} 1133 stgt2 := []string{"-", "2.718280076980591", "9", "5", "3.1415927410125732"} 1134 itgt := []int{17} 1135 itgt2 := []int{13} 1136 1137 // itgt2 := []int{17} 1138 // ftgt2 := []float64{17} 1139 1140 cases := []evendeep.TestCase{ 1141 evendeep.NewTestCase( 1142 "slice (uint64 -> string)", 1143 []uint64{9, 5}, &stgt, 1144 &[]string{"-", "2.718280076980591", "9", "5"}, 1145 []evendeep.Opt{evendeep.WithMergeStrategyOpt}, 1146 nil, 1147 ), 1148 evendeep.NewTestCase( 1149 "slice (float32 -> string)", 1150 []float32{math.Pi, 2.71828}, &stgt, 1151 // NOTE that stgt kept the new result in last subtest 1152 &stgt2, 1153 []evendeep.Opt{evendeep.WithMergeStrategyOpt}, 1154 nil, 1155 ), 1156 evendeep.NewTestCase( 1157 "slice (string(with floats) -> int)", 1158 stgt2, &itgt, 1159 &[]int{17, 3, 9, 5}, 1160 []evendeep.Opt{evendeep.WithMergeStrategyOpt}, 1161 nil, 1162 ), 1163 evendeep.NewTestCase( 1164 "slice (string(with floats) -> int)", 1165 []string{"-", "9.718280076980591", "9", "5", "3.1415927410125732"}, 1166 &itgt2, 1167 &[]int{13, 10, 9, 5, 3}, 1168 []evendeep.Opt{evendeep.WithMergeStrategyOpt}, 1169 nil, 1170 ), 1171 1172 // needs complexToAnythingConverter 1173 1174 // evendeep.NewTestCase( 1175 // "slice (complex -> float64)", 1176 // []complex64{math.Pi + 3i, 2.71828 + 4.19i}, 1177 // &ftgt2, 1178 // // NOTE that stgt kept the new result in last subtest 1179 // &[]float64{2.718280076980591, 17, 3.1415927410125732}, 1180 // []evendeep.Opt{evendeep.WithMergeStrategy}, 1181 // nil, 1182 // ), 1183 // evendeep.NewTestCase( 1184 // "slice (complex -> int)", 1185 // []complex64{math.Pi + 3i, 2.71828 + 4.19i}, 1186 // &itgt2, 1187 // // NOTE that stgt kept the new result in last subtest 1188 // &[]float64{3, 17}, 1189 // []evendeep.Opt{evendeep.WithMergeStrategy}, 1190 // nil, 1191 // ), 1192 } 1193 1194 for ix, tc := range cases { 1195 if !t.Run(fmt.Sprintf("%3d. %s", ix, tc.Description), evendeep.DefaultDeepCopyTestRunner(ix, tc)) { 1196 break 1197 } 1198 } 1199 } 1200 1201 func TestMapSimple(t *testing.T) { 1202 1203 src := map[int64]float64{7: 0, 3: 7.18} 1204 tgt := map[int]float32{1: 3.1, 2: 4.5, 3: 9.67} 1205 exp := map[int]float32{1: 3.1, 2: 4.5, 3: 7.18, 7: 0} 1206 1207 cases := []evendeep.TestCase{ 1208 evendeep.NewTestCase( 1209 "map (map[int64]float64 -> map[int]float32)", 1210 src, &tgt, &exp, 1211 []evendeep.Opt{evendeep.WithMergeStrategyOpt, evendeep.WithAutoExpandStructOpt}, 1212 nil, 1213 ), 1214 // evendeep.NewTestCase( 1215 // "slice (uint64 -> int)", 1216 // []uint64{9, 5}, &itgt, &[]int{13, 5, 9}, 1217 // []evendeep.Opt{evendeep.WithMergeStrategy}, 1218 // nil, 1219 // ), 1220 } 1221 1222 for ix, tc := range cases { 1223 if !t.Run(fmt.Sprintf("%3d. %s", ix, tc.Description), evendeep.DefaultDeepCopyTestRunner(ix, tc)) { 1224 break 1225 } 1226 } 1227 } 1228 1229 func TestMapAndStruct(t *testing.T) { 1230 1231 timeZone, _ := time.LoadLocation("America/Phoenix") 1232 timeZone2, _ := time.LoadLocation("Asia/Chongqing") 1233 tm := time.Date(1999, 3, 13, 5, 57, 11, 1901, timeZone) 1234 tm2 := time.Date(2003, 9, 1, 23, 59, 59, 3579, timeZone) 1235 tm1 := time.Date(2021, 2, 28, 13, 1, 23, 800, timeZone2) 1236 tm3 := time.Date(2015, 1, 29, 19, 31, 37, 77, timeZone2) 1237 1238 src := evendeep.Employee2{ 1239 Base: evendeep.Base{ 1240 Name: "Bob", 1241 Birthday: &tm, 1242 Age: 24, 1243 EmployeID: 7, 1244 }, 1245 Avatar: "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet&rs=1", 1246 Image: []byte{95, 27, 43, 66, 0, 21, 210}, 1247 Attr: &evendeep.Attr{Attrs: []string{helloString, worldString}}, 1248 Valid: true, 1249 } 1250 1251 src3 := evendeep.Employee2{ 1252 Base: evendeep.Base{ 1253 Name: "Ellen", 1254 Birthday: &tm2, 1255 Age: 55, 1256 EmployeID: 9, 1257 }, 1258 Avatar: "https://placeholder.com/225x168", 1259 Image: []byte{181, 130, 23}, 1260 Attr: &evendeep.Attr{Attrs: []string{"god", "bless"}}, 1261 Valid: false, 1262 Deleted: true, 1263 } 1264 1265 tgt := evendeep.User{ 1266 Name: "Mathews", 1267 Birthday: &tm3, 1268 Age: 3, 1269 EmployeID: 92, 1270 Attr: &evendeep.Attr{Attrs: []string{"get"}}, 1271 Deleted: false, 1272 } 1273 1274 tgt2 := evendeep.User{ 1275 Name: "Frank", 1276 Birthday: &tm2, 1277 Age: 18, 1278 EmployeID: 9, 1279 Attr: &evendeep.Attr{Attrs: []string{"baby"}}, 1280 } 1281 1282 tgt3 := evendeep.User{ 1283 Name: "Zeuth", 1284 Birthday: &tm1, 1285 Age: 31, 1286 EmployeID: 17, 1287 Image: []byte{181, 130, 29}, 1288 Attr: &evendeep.Attr{Attrs: []string{"you"}}, 1289 } 1290 1291 expect1 := evendeep.User{ 1292 Name: "Bob", 1293 Birthday: &tm, 1294 Age: 24, 1295 EmployeID: 7, 1296 Avatar: "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet&rs=1", 1297 Image: []byte{95, 27, 43, 66, 0, 21, 210}, 1298 Attr: &evendeep.Attr{Attrs: []string{"get", helloString, worldString}}, 1299 Valid: true, 1300 } 1301 1302 expect3 := evendeep.User{ 1303 Name: "Ellen", 1304 Birthday: &tm2, 1305 Age: 55, 1306 EmployeID: 9, 1307 Avatar: "https://placeholder.com/225x168", 1308 Image: []byte{181, 130, 29, 23}, 1309 Attr: &evendeep.Attr{Attrs: []string{"you", "god", "bless"}}, 1310 Deleted: true, 1311 } 1312 1313 srcmap := map[int64]*evendeep.Employee2{ 1314 7: &src, 1315 3: &src3, 1316 } 1317 tgtmap := map[float32]*evendeep.User{ 1318 7: &tgt, 1319 2: &tgt2, 1320 3: &tgt3, 1321 } 1322 expmap := map[float32]*evendeep.User{ 1323 7: &expect1, 1324 2: &tgt2, 1325 3: &expect3, 1326 } 1327 1328 cases := []evendeep.TestCase{ 1329 evendeep.NewTestCase( 1330 "map (map[int64]Employee2 -> map[int]User)", 1331 srcmap, &tgtmap, &expmap, 1332 []evendeep.Opt{ 1333 evendeep.WithMergeStrategyOpt, 1334 evendeep.WithAutoExpandStructOpt, 1335 evendeep.WithAutoNewForStructFieldOpt, 1336 }, 1337 nil, 1338 ), 1339 // evendeep.NewTestCase( 1340 // "slice (uint64 -> int)", 1341 // []uint64{9, 5}, &itgt, &[]int{13, 5, 9}, 1342 // []evendeep.Opt{evendeep.WithMergeStrategy}, 1343 // nil, 1344 // ), 1345 } 1346 1347 for ix, tc := range cases { 1348 if !t.Run(fmt.Sprintf("%3d. %s", ix, tc.Description), evendeep.DefaultDeepCopyTestRunner(ix, tc)) { 1349 break 1350 } 1351 } 1352 } 1353 1354 func TestMapToString(t *testing.T) { 1355 1356 timeZone, _ := time.LoadLocation("America/Phoenix") 1357 tm := time.Date(1999, 3, 13, 5, 57, 11, 1901, timeZone) 1358 // timeZone2, _ := time.LoadLocation("Asia/Chongqing") 1359 // tm1 := time.Date(2021, 2, 28, 13, 1, 23, 800, timeZone2) 1360 // tm2 := time.Date(2003, 9, 1, 23, 59, 59, 3579, timeZone) 1361 // tm3 := time.Date(2015, 1, 29, 19, 31, 37, 77, timeZone2) 1362 1363 expect2 := evendeep.User{ 1364 Name: "Bob", 1365 Birthday: &tm, 1366 Age: 24, 1367 EmployeID: 7, 1368 Avatar: "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet&rs=1", 1369 Image: []byte{95, 27, 43, 66, 0, 21, 210}, 1370 Attr: &evendeep.Attr{Attrs: []string{helloString, worldString}}, 1371 Valid: true, 1372 } 1373 1374 expect3 := evendeep.Employee2{ 1375 Base: evendeep.Base{ 1376 Name: "Bob", 1377 Birthday: &tm, 1378 Age: 24, 1379 EmployeID: 7, 1380 }, 1381 Avatar: "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet&rs=1", 1382 Image: []byte{95, 27, 43, 66, 0, 21, 210}, 1383 Attr: &evendeep.Attr{Attrs: []string{helloString, worldString}}, 1384 Valid: true, 1385 } 1386 1387 var s2 evendeep.User 1388 var s3 evendeep.Employee2 1389 var str1 string 1390 1391 // var map1 = make(map[string]interface{}) 1392 var map1 = map[string]interface{}{ 1393 "Name": "Bob", 1394 "Birthday": tm, 1395 "Age": 24, 1396 "EmployeID": int64(7), 1397 "Avatar": "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet&rs=1", 1398 "Image": []byte{95, 27, 43, 66, 0, 21, 210}, 1399 "Attr": map[string]interface{}{"Attrs": []string{helloString, worldString}}, 1400 "Valid": true, 1401 "Deleted": false, 1402 } 1403 1404 expect1 := `{ 1405 "Age": 24, 1406 "Attr": { 1407 "Attrs": [ 1408 "hello", 1409 "world" 1410 ] 1411 }, 1412 "Avatar": "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet\u0026rs=1", 1413 "Birthday": "1999-03-13T05:57:11.000001901-07:00", 1414 "Deleted": false, 1415 "EmployeID": 7, 1416 "Image": "XxsrQgAV0g==", 1417 "Name": "Bob", 1418 "Valid": true 1419 }` 1420 1421 cases := []evendeep.TestCase{ 1422 evendeep.NewTestCase( 1423 "map -> string [json]", 1424 map1, &str1, &expect1, 1425 []evendeep.Opt{ 1426 evendeep.WithStringMarshaller(func(v interface{}) ([]byte, error) { 1427 return json.MarshalIndent(v, "", " ") 1428 }), 1429 evendeep.WithMergeStrategyOpt, 1430 evendeep.WithAutoExpandStructOpt}, 1431 nil, 1432 ), 1433 1434 evendeep.NewTestCase( 1435 "map -> struct User", 1436 map1, &s2, &expect2, 1437 []evendeep.Opt{evendeep.WithMergeStrategyOpt, evendeep.WithAutoExpandStructOpt}, 1438 nil, 1439 ), 1440 evendeep.NewTestCase( 1441 "map -> struct Employee2", 1442 map1, &s3, &expect3, 1443 []evendeep.Opt{evendeep.WithMergeStrategyOpt, evendeep.WithAutoExpandStructOpt}, 1444 nil, 1445 ), 1446 } 1447 1448 for ix, tc := range cases { 1449 if !t.Run(fmt.Sprintf("%3d. %s", ix, tc.Description), evendeep.DefaultDeepCopyTestRunner(ix, tc)) { 1450 break 1451 } 1452 } 1453 } 1454 1455 func testIfBadCopy(t *testing.T, src, tgt, result interface{}, title string, notFailed ...interface{}) { 1456 1457 t.Logf("checking result ...") 1458 1459 // if diff := deep.Equal(src, tgt); diff == nil { 1460 // return 1461 // } else { 1462 // t.Fatalf("testIfBadCopy - BAD COPY (%v):\n SRC: %+v\n TGT: %+v\n\n DIFF: \n%v", title, src, tgt, diff) 1463 // } 1464 1465 // dd := deepdiff.New() 1466 // diff, err := dd.Diff(context.Background(), src, tgt) 1467 // if err != nil { 1468 // return 1469 // } 1470 // if diff.Len() > 0 { 1471 // t.Fatalf("testIfBadCopy - BAD COPY (%v):\n SRC: %+v\n TGT: %+v\n\n DIFF: \n%v", title, src, tgt, diff) 1472 // } else { 1473 // return 1474 // } 1475 1476 dif, equal := diff.New(src, tgt) 1477 if equal { 1478 return 1479 } 1480 1481 fmt.Println(dif) 1482 err := errors.New("diff.PrettyDiff identified its not equal:\ndifferent:\n%v", dif) 1483 1484 for _, b := range notFailed { 1485 if yes, ok := b.(bool); yes && ok { 1486 return 1487 } 1488 } 1489 1490 t.Fatal(err) 1491 1492 // if !reflect.DeepEqual(src, tgt) { 1493 // 1494 // var b1, b2 []byte 1495 // var err error 1496 // if b1, err = json.MarshalIndent(src, "", " "); err == nil { 1497 // if b2, err = json.MarshalIndent(src, "", " "); err == nil { 1498 // if string(b1) == string(b2) { 1499 // return 1500 // } 1501 // t.Logf("testIfBadCopy - src: %v\ntgt: %v\n", string(b1), string(b2)) 1502 // } 1503 // } 1504 // if err != nil { 1505 // t.Logf("testIfBadCopy - json marshal not ok (just a warning): %v", err) 1506 // 1507 // //if b1, err = yaml.Marshal(src); err == nil { 1508 // // if b2, err = yaml.Marshal(src); err == nil { 1509 // // if string(b1) == string(b2) { 1510 // // return 1511 // // } 1512 // // } 1513 // //} 1514 // 1515 // //gob.Register(X1{}) 1516 // // 1517 // //buf1 := new(bytes.Buffer) 1518 // //enc1 := gob.NewEncoder(buf1) 1519 // //if err = enc1.Encode(&src); err != nil { 1520 // // t.Fatal(err) 1521 // //} 1522 // // 1523 // //buf2 := new(bytes.Buffer) 1524 // //enc2 := gob.NewEncoder(buf2) 1525 // //if err = enc2.Encode(&tgt); err != nil { 1526 // // t.Fatal(err) 1527 // //} 1528 // // 1529 // //s1, s2 := buf1.String(), buf2.String() 1530 // //if s1 == s2 { 1531 // // return 1532 // //} 1533 // } 1534 // 1535 // for _, b := range notFailed { 1536 // if yes, ok := b.(bool); yes && ok { 1537 // return 1538 // } 1539 // } 1540 // 1541 // t.Fatalf("testIfBadCopy - BAD COPY (%v):\n SRC: %+v\n TGT: %+v\n RES: %v", title, src, tgt, result) 1542 // } 1543 } 1544 1545 func TestExample1(t *testing.T) { 1546 timeZone, _ := time.LoadLocation("America/Phoenix") 1547 tm := time.Date(1999, 3, 13, 5, 57, 11, 1901, timeZone) 1548 var src = evendeep.Employee2{ 1549 Base: evendeep.Base{ 1550 Name: "Bob", 1551 Birthday: &tm, 1552 Age: 24, 1553 EmployeID: 7, 1554 }, 1555 Avatar: "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet&rs=1", 1556 Image: []byte{95, 27, 43, 66, 0, 21, 210}, 1557 Attr: &evendeep.Attr{Attrs: []string{helloString, worldString}}, 1558 Valid: true, 1559 } 1560 var dst evendeep.User 1561 1562 // direct way but no error report: evendeep.DeepCopy(src, &dst) 1563 c := evendeep.New() 1564 if err := c.CopyTo(src, &dst); err != nil { 1565 t.Fatal(err) 1566 } 1567 if !reflect.DeepEqual(dst, evendeep.User{ 1568 Name: "Bob", 1569 Birthday: &tm, 1570 Age: 24, 1571 EmployeID: 7, 1572 Avatar: "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet&rs=1", 1573 Image: []byte{95, 27, 43, 66, 0, 21, 210}, 1574 Attr: &evendeep.Attr{Attrs: []string{helloString, worldString}}, 1575 Valid: true, 1576 }) { 1577 t.Fatalf("bad, got %v", dst) 1578 } 1579 } 1580 1581 type MyType struct { 1582 I int 1583 } 1584 1585 type MyTypeToStringConverter struct{} 1586 1587 // Uncomment this line if you wanna take a ValueCopier implementation too: 1588 // func (c *MyTypeToStringConverter) CopyTo(ctx *evendeep.ValueConverterContext, source, target reflect.Value) (err error) { return } 1589 1590 func (c *MyTypeToStringConverter) Transform(ctx *evendeep.ValueConverterContext, source reflect.Value, targetType reflect.Type) (target reflect.Value, err error) { 1591 if source.IsValid() && targetType.Kind() == reflect.String { 1592 var str string 1593 if str, err = evendeep.FallbackToBuiltinStringMarshalling(source); err == nil { 1594 target = reflect.ValueOf(str) 1595 } 1596 } 1597 return 1598 } 1599 1600 func (c *MyTypeToStringConverter) Match(params *evendeep.Params, source, target reflect.Type) (ctx *evendeep.ValueConverterContext, yes bool) { 1601 sn, sp := source.Name(), source.PkgPath() 1602 sk, tk := source.Kind(), target.Kind() 1603 if yes = sk == reflect.Struct && tk == reflect.String && 1604 sn == "MyType" && sp == "github.com/hedzr/evendeep_test"; yes { 1605 ctx = &evendeep.ValueConverterContext{Params: params} 1606 } 1607 return 1608 } 1609 1610 func TestExample2(t *testing.T) { 1611 var myData = MyType{I: 9} 1612 var dst string 1613 c := evendeep.NewForTest() 1614 _ = c.CopyTo(myData, &dst, 1615 evendeep.WithValueConverters(&MyTypeToStringConverter{}), 1616 evendeep.WithStringMarshaller(json.Marshal), 1617 ) 1618 if dst != `{"I":9}` { 1619 t.Fatalf("bad 1, got %v", dst) 1620 } 1621 1622 // a stub call for coverage 1623 evendeep.RegisterDefaultCopiers() 1624 1625 var dst1 string 1626 evendeep.RegisterDefaultConverters(&MyTypeToStringConverter{}) 1627 c = evendeep.NewForTest() 1628 _ = c.CopyTo(myData, &dst1, 1629 evendeep.WithStringMarshaller(json.Marshal), 1630 ) 1631 if dst1 != `{"I":9}` { 1632 t.Fatalf("bad 2, got %v", dst) 1633 } 1634 1635 } 1636 1637 func TestExample3(t *testing.T) { 1638 timeZone, _ := time.LoadLocation("America/Phoenix") 1639 tm := time.Date(1999, 3, 13, 5, 57, 11, 1901, timeZone) 1640 var originRec = evendeep.User{ 1641 Name: "Bob", 1642 Birthday: &tm, 1643 Age: 24, 1644 EmployeID: 7, 1645 Avatar: "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet&rs=1", 1646 Image: []byte{95, 27, 43, 66, 0, 21, 210}, 1647 Attr: &evendeep.Attr{Attrs: []string{helloString, worldString}}, 1648 Valid: true, 1649 } 1650 var newRecord evendeep.User 1651 var t0 = time.Unix(0, 0) 1652 var expectRec = evendeep.User{Name: "Barbara", Birthday: &t0, Attr: &evendeep.Attr{}} 1653 1654 _ = evendeep.New().CopyTo(originRec, &newRecord) 1655 t.Logf("newRecord: %v", newRecord) 1656 1657 newRecord.Name = "Barbara" 1658 _ = evendeep.New().CopyTo(originRec, &newRecord, evendeep.WithORMDiffOpt) 1659 if len(newRecord.Attr.Attrs) == len(expectRec.Attr.Attrs) { 1660 newRecord.Attr = expectRec.Attr 1661 } 1662 if newRecord.Birthday == nil || newRecord.Birthday.Nanosecond() == 0 { 1663 newRecord.Birthday = &t0 1664 } 1665 if !reflect.DeepEqual(newRecord, expectRec) { 1666 t.Fatalf("bad, got %v | %v", newRecord, newRecord.Birthday.Nanosecond()) 1667 } 1668 t.Logf("newRecord: %v", newRecord) 1669 1670 } 1671 1672 func TestExample4(t *testing.T) { 1673 timeZone, _ := time.LoadLocation("America/Phoenix") 1674 attr := &evendeep.Attr{Attrs: []string{helloString, worldString}} 1675 tm := time.Date(1999, 3, 13, 5, 57, 11, 1901, timeZone) 1676 var originRec = evendeep.User{ 1677 Name: "Bob", 1678 Birthday: &tm, 1679 Age: 24, 1680 EmployeID: 7, 1681 Avatar: "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet&rs=1", 1682 Image: []byte{95, 27, 43, 66, 0, 21, 210}, 1683 Attr: attr, 1684 Valid: true, 1685 } 1686 var dstRecord = new(evendeep.User) 1687 var t0 = time.Unix(0, 0) 1688 var emptyRecord = evendeep.User{Name: "Barbara", Birthday: &t0} 1689 var expectRecord = &evendeep.User{Name: "Barbara", Birthday: &t0, 1690 Age: 24, 1691 EmployeID: 7, 1692 Avatar: "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet&rs=1", 1693 Image: []byte{95, 27, 43, 66, 0, 21, 210}, 1694 Attr: attr, 1695 Valid: true, 1696 } 1697 // var expectRecordZero = evendeep.User{Name: "Barbara", Birthday: &t0, 1698 // // Image: []byte{95, 27, 43, 66, 0, 21, 210}, 1699 // Attr: &evendeep.Attr{}, 1700 // // Attr: &evendeep.Attr{Attrs: []string{"hello", worldString}, 1701 // // Valid: true, 1702 // } 1703 1704 evendeep.ResetDefaultCopyController() 1705 1706 // prepare a hard copy at first 1707 evendeep.DeepCopy(originRec, &dstRecord) 1708 t.Logf("dstRecord: %v", dstRecord) 1709 dbglog.Log("---- dstRecord: %v", dstRecord) 1710 if !evendeep.DeepEqual(dstRecord, &originRec) { 1711 t.Fatalf("bad, \n got: %v\nexpect: %v\n got.Attr: %v\nexpect.Attr: %v", dstRecord, originRec, dstRecord.Attr, originRec.Attr) 1712 } 1713 1714 // now update dstRecord with the non-empty fields. 1715 evendeep.DeepCopy(emptyRecord, &dstRecord, evendeep.WithOmitEmptyOpt) 1716 t.Logf("dstRecord (WithOmitEmptyOpt): %v", dstRecord) 1717 // if !evendeep.DeepEqual(dstRecord, expectRecord) { 1718 // t.Fatalf("bad, \n got: %v\nexpect: %v\n got.Attr: %v\nexpect.Attr: %v", dstRecord, expectRecord, dstRecord.Attr, expectRecord.Attr) 1719 // } 1720 if delta, equal := evendeep.DeepDiff(dstRecord, expectRecord); !equal { 1721 t.Fatalf("bad, \n got: %v\nexpect: %v\n delta:\n%v", dstRecord, expectRecord, delta) 1722 } 1723 1724 // evendeep.ResetDefaultCopyController() 1725 // // now update dstRecord with the non-empty fields. 1726 // evendeep.DeepCopy(emptyRecord, &dstRecord, evendeep.WithStrategies(cms.OmitIfEmpty)) 1727 // t.Logf("dstRecord (ClearIfInvalid): %v", dstRecord) 1728 // if delta, equal := evendeep.DeepDiff(dstRecord, expectRecordZero); !equal { 1729 // t.Fatalf("bad, \n got: %v\nexpect: %v\n delta:\n%v", dstRecord, expectRecordZero, delta) 1730 // } 1731 }