package copier_test import ( "database/sql" "errors" "fmt" "reflect" "testing" "time" "github.com/jinzhu/copier" ) type User struct { Name string Birthday *time.Time Nickname string Role string Age int32 FakeAge *int32 Notes []string flags []byte } func (user User) DoubleAge() int32 { return 2 * user.Age } type Employee struct { _User *User Name string Birthday *time.Time NickName *string Age int64 FakeAge int EmployeID int64 DoubleAge int32 SuperRule string Notes []*string flags []byte } func (employee *Employee) Role(role string) { employee.SuperRule = "Super " + role } func checkEmployee(employee Employee, user User, t *testing.T, testCase string) { if employee.Name != user.Name { t.Errorf("%v: Name haven't been copied correctly.", testCase) } if employee.NickName == nil || *employee.NickName != user.Nickname { t.Errorf("%v: NickName haven't been copied correctly.", testCase) } if employee.Birthday == nil && user.Birthday != nil { t.Errorf("%v: Birthday haven't been copied correctly.", testCase) } if employee.Birthday != nil && user.Birthday == nil { t.Errorf("%v: Birthday haven't been copied correctly.", testCase) } if employee.Birthday != nil && user.Birthday != nil && !employee.Birthday.Equal(*(user.Birthday)) { t.Errorf("%v: Birthday haven't been copied correctly.", testCase) } if employee.Age != int64(user.Age) { t.Errorf("%v: Age haven't been copied correctly.", testCase) } if user.FakeAge != nil && employee.FakeAge != int(*user.FakeAge) { t.Errorf("%v: FakeAge haven't been copied correctly.", testCase) } if employee.DoubleAge != user.DoubleAge() { t.Errorf("%v: Copy from method doesn't work", testCase) } if employee.SuperRule != "Super "+user.Role { t.Errorf("%v: Copy to method doesn't work", testCase) } if len(employee.Notes) != len(user.Notes) { t.Fatalf("%v: Copy from slice doesn't work, employee notes len: %v, user: %v", testCase, len(employee.Notes), len(user.Notes)) } for idx, note := range user.Notes { if note != *employee.Notes[idx] { t.Fatalf("%v: Copy from slice doesn't work, notes idx: %v employee: %v user: %v", testCase, idx, *employee.Notes[idx], note) } } } func TestCopySameStructWithPointerField(t *testing.T) { var fakeAge int32 = 12 var currentTime time.Time = time.Now() user := &User{Birthday: ¤tTime, Name: "Jinzhu", Nickname: "jinzhu", Age: 18, FakeAge: &fakeAge, Role: "Admin", Notes: []string{"hello world", "welcome"}, flags: []byte{'x'}} newUser := &User{} copier.Copy(newUser, user) if user.Birthday == newUser.Birthday { t.Errorf("TestCopySameStructWithPointerField: copy Birthday failed since they need to have different address") } if user.FakeAge == newUser.FakeAge { t.Errorf("TestCopySameStructWithPointerField: copy FakeAge failed since they need to have different address") } } func checkEmployee2(employee Employee, user *User, t *testing.T, testCase string) { if user == nil { if employee.Name != "" || employee.NickName != nil || employee.Birthday != nil || employee.Age != 0 || employee.DoubleAge != 0 || employee.FakeAge != 0 || employee.SuperRule != "" || employee.Notes != nil { t.Errorf("%v : employee should be empty", testCase) } return } checkEmployee(employee, *user, t, testCase) } func TestCopyStruct(t *testing.T) { var fakeAge int32 = 12 user := User{Name: "Jinzhu", Nickname: "jinzhu", Age: 18, FakeAge: &fakeAge, Role: "Admin", Notes: []string{"hello world", "welcome"}, flags: []byte{'x'}} employee := Employee{} if err := copier.Copy(employee, &user); err == nil { t.Errorf("Copy to unaddressable value should get error") } copier.Copy(&employee, &user) checkEmployee(employee, user, t, "Copy From Ptr To Ptr") employee2 := Employee{} copier.Copy(&employee2, user) checkEmployee(employee2, user, t, "Copy From Struct To Ptr") employee3 := Employee{} ptrToUser := &user copier.Copy(&employee3, &ptrToUser) checkEmployee(employee3, user, t, "Copy From Double Ptr To Ptr") employee4 := &Employee{} copier.Copy(&employee4, user) checkEmployee(*employee4, user, t, "Copy From Ptr To Double Ptr") employee5 := &Employee{} copier.Copy(&employee5, &employee) checkEmployee(*employee5, user, t, "Copy From Employee To Employee") } func TestCopyFromStructToSlice(t *testing.T) { user := User{Name: "Jinzhu", Age: 18, Role: "Admin", Notes: []string{"hello world"}} employees := []Employee{} if err := copier.Copy(employees, &user); err != nil && len(employees) != 0 { t.Errorf("Copy to unaddressable value should get error") } if copier.Copy(&employees, &user); len(employees) != 1 { t.Errorf("Should only have one elem when copy struct to slice") } else { checkEmployee(employees[0], user, t, "Copy From Struct To Slice Ptr") } employees2 := &[]Employee{} if copier.Copy(&employees2, user); len(*employees2) != 1 { t.Errorf("Should only have one elem when copy struct to slice") } else { checkEmployee((*employees2)[0], user, t, "Copy From Struct To Double Slice Ptr") } employees3 := []*Employee{} if copier.Copy(&employees3, user); len(employees3) != 1 { t.Errorf("Should only have one elem when copy struct to slice") } else { checkEmployee(*(employees3[0]), user, t, "Copy From Struct To Ptr Slice Ptr") } employees4 := &[]*Employee{} if copier.Copy(&employees4, user); len(*employees4) != 1 { t.Errorf("Should only have one elem when copy struct to slice") } else { checkEmployee(*((*employees4)[0]), user, t, "Copy From Struct To Double Ptr Slice Ptr") } } func TestCopyFromSliceToSlice(t *testing.T) { users := []User{ {Name: "Jinzhu", Age: 18, Role: "Admin", Notes: []string{"hello world"}}, {Name: "Jinzhu2", Age: 22, Role: "Dev", Notes: []string{"hello world", "hello"}}} employees := []Employee{} if copier.Copy(&employees, users); len(employees) != 2 { t.Errorf("Should have two elems when copy slice to slice") } else { checkEmployee(employees[0], users[0], t, "Copy From Slice To Slice Ptr @ 1") checkEmployee(employees[1], users[1], t, "Copy From Slice To Slice Ptr @ 2") } employees2 := &[]Employee{} if copier.Copy(&employees2, &users); len(*employees2) != 2 { t.Errorf("Should have two elems when copy slice to slice") } else { checkEmployee((*employees2)[0], users[0], t, "Copy From Slice Ptr To Double Slice Ptr @ 1") checkEmployee((*employees2)[1], users[1], t, "Copy From Slice Ptr To Double Slice Ptr @ 2") } employees3 := []*Employee{} if copier.Copy(&employees3, users); len(employees3) != 2 { t.Errorf("Should have two elems when copy slice to slice") } else { checkEmployee(*(employees3[0]), users[0], t, "Copy From Slice To Ptr Slice Ptr @ 1") checkEmployee(*(employees3[1]), users[1], t, "Copy From Slice To Ptr Slice Ptr @ 2") } employees4 := &[]*Employee{} if copier.Copy(&employees4, users); len(*employees4) != 2 { t.Errorf("Should have two elems when copy slice to slice") } else { checkEmployee(*((*employees4)[0]), users[0], t, "Copy From Slice Ptr To Double Ptr Slice Ptr @ 1") checkEmployee(*((*employees4)[1]), users[1], t, "Copy From Slice Ptr To Double Ptr Slice Ptr @ 2") } } func TestCopyFromSliceToSlice2(t *testing.T) { users := []*User{{Name: "Jinzhu", Age: 18, Role: "Admin", Notes: []string{"hello world"}}, nil} employees := []Employee{} if copier.Copy(&employees, users); len(employees) != 2 { t.Errorf("Should have two elems when copy slice to slice") } else { checkEmployee2(employees[0], users[0], t, "Copy From Slice To Slice Ptr @ 1") checkEmployee2(employees[1], users[1], t, "Copy From Slice To Slice Ptr @ 2") } employees2 := &[]Employee{} if copier.Copy(&employees2, &users); len(*employees2) != 2 { t.Errorf("Should have two elems when copy slice to slice") } else { checkEmployee2((*employees2)[0], users[0], t, "Copy From Slice Ptr To Double Slice Ptr @ 1") checkEmployee2((*employees2)[1], users[1], t, "Copy From Slice Ptr To Double Slice Ptr @ 2") } employees3 := []*Employee{} if copier.Copy(&employees3, users); len(employees3) != 2 { t.Errorf("Should have two elems when copy slice to slice") } else { checkEmployee2(*(employees3[0]), users[0], t, "Copy From Slice To Ptr Slice Ptr @ 1") checkEmployee2(*(employees3[1]), users[1], t, "Copy From Slice To Ptr Slice Ptr @ 2") } employees4 := &[]*Employee{} if copier.Copy(&employees4, users); len(*employees4) != 2 { t.Errorf("Should have two elems when copy slice to slice") } else { checkEmployee2(*((*employees4)[0]), users[0], t, "Copy From Slice Ptr To Double Ptr Slice Ptr @ 1") checkEmployee2(*((*employees4)[1]), users[1], t, "Copy From Slice Ptr To Double Ptr Slice Ptr @ 2") } } func TestCopyFromSliceToSlice3(t *testing.T) { type CollectionAlias struct { CollectionName string `json:"collection_name"` Name string `json:"name"` } expectedResult := []*CollectionAlias{ {"collection", "collection_alias1"}, {"collection", "collection_alias2"}, {"collection", "collection_alias3"}, } mockedResult := []*CollectionAlias{} copier.Copy(&mockedResult, &expectedResult) if len(mockedResult) != len(expectedResult) { t.Fatalf("failed to copy results") } for idx := range mockedResult { if mockedResult[idx].Name != mockedResult[idx].Name || mockedResult[idx].CollectionName != mockedResult[idx].CollectionName { t.Fatalf("failed to copy results") } } } func TestEmbeddedAndBase(t *testing.T) { type Base struct { BaseField1 int BaseField2 int User *User } type Embed struct { EmbedField1 int EmbedField2 int Base } base := Base{} embedded := Embed{} embedded.BaseField1 = 1 embedded.BaseField2 = 2 embedded.EmbedField1 = 3 embedded.EmbedField2 = 4 user := User{ Name: "testName", } embedded.User = &user copier.Copy(&base, &embedded) if base.BaseField1 != 1 || base.User.Name != "testName" { t.Error("Embedded fields not copied") } base.BaseField1 = 11 base.BaseField2 = 12 user1 := User{ Name: "testName1", } base.User = &user1 copier.Copy(&embedded, &base) if embedded.BaseField1 != 11 || embedded.User.Name != "testName1" { t.Error("base fields not copied") } } func TestStructField(t *testing.T) { type Detail struct { Info1 string Info2 *string } type SimilarDetail struct { Info1 string Info2 *string } type UserWithDetailsPtr struct { Details []*Detail Detail *Detail Notes *[]string Notes2 *[]string } type UserWithDetails struct { Details []Detail Detail Detail Notes []string Notes2 []string } type UserWithSimilarDetailsPtr struct { Detail *SimilarDetail } type UserWithSimilarDetails struct { Detail SimilarDetail } type EmployeeWithDetails struct { Detail Detail } type EmployeeWithDetailsPtr struct { Detail *Detail } type EmployeeWithSimilarDetails struct { Detail SimilarDetail } type EmployeeWithSimilarDetailsPtr struct { Detail *SimilarDetail } optionsDeepCopy := copier.Option{ DeepCopy: true, } checkDetail := func(t *testing.T, source Detail, target Detail) { if source.Info1 != target.Info1 { t.Errorf("info1 is diff: source: %v, target: %v", source.Info1, target.Info1) } if (source.Info2 != nil || target.Info2 != nil) && (*source.Info2 != *target.Info2) { t.Errorf("info2 is diff: source: %v, target: %v", *source.Info2, *target.Info2) } } t.Run("Should work without deepCopy", func(t *testing.T) { t.Run("Should work with same type and both ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetailsPtr{ Detail: &Detail{Info1: "hello", Info2: &info2}, Details: []*Detail{{Info1: "hello", Info2: &info2}}, } to := UserWithDetailsPtr{} copier.Copy(&to, from) checkDetail(t, *from.Detail, *to.Detail) *to.Detail.Info2 = "new value" if *from.Detail.Info2 != *to.Detail.Info2 { t.Fatalf("DeepCopy not enabled") } if len(from.Details) != len(to.Details) { t.Fatalf("slice should be copied") } for idx, detail := range from.Details { checkDetail(t, *detail, *to.Details[idx]) } }) t.Run("Should work with same type and both not ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetails{ Detail: Detail{Info1: "hello", Info2: &info2}, Details: []Detail{{Info1: "hello", Info2: &info2}}, } to := UserWithDetails{} copier.Copy(&to, from) checkDetail(t, from.Detail, to.Detail) *to.Detail.Info2 = "new value" if *from.Detail.Info2 != *to.Detail.Info2 { t.Fatalf("DeepCopy not enabled") } if len(from.Details) != len(to.Details) { t.Fatalf("slice should be copied") } for idx, detail := range from.Details { checkDetail(t, detail, to.Details[idx]) } }) t.Run("Should work with different type and both ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetailsPtr{Detail: &Detail{Info1: "hello", Info2: &info2}} to := EmployeeWithDetailsPtr{} copier.Copy(&to, from) newValue := "new value" to.Detail.Info2 = &newValue if to.Detail.Info1 == "" { t.Errorf("should not be empty") } if to.Detail.Info1 != from.Detail.Info1 { t.Errorf("should be the same") } if to.Detail.Info2 == from.Detail.Info2 { t.Errorf("should be different") } }) t.Run("Should work with different type and both not ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetails{Detail: Detail{Info1: "hello", Info2: &info2}} to := EmployeeWithDetails{} copier.Copy(&to, from) newValue := "new value" to.Detail.Info2 = &newValue if to.Detail.Info1 == "" { t.Errorf("should not be empty") } if to.Detail.Info1 != from.Detail.Info1 { t.Errorf("should be the same") } if to.Detail.Info2 == from.Detail.Info2 { t.Errorf("should be different") } }) t.Run("Should work with from ptr field and to not ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetailsPtr{Detail: &Detail{Info1: "hello", Info2: &info2}} to := EmployeeWithDetails{} copier.Copy(&to, from) newValue := "new value" to.Detail.Info2 = &newValue if to.Detail.Info1 == "" { t.Errorf("should not be empty") } if to.Detail.Info1 != from.Detail.Info1 { t.Errorf("should be the same") } if to.Detail.Info2 == from.Detail.Info2 { t.Errorf("should be different") } }) t.Run("Should work with from not ptr field and to ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetails{Detail: Detail{Info1: "hello", Info2: &info2}} to := EmployeeWithDetailsPtr{} copier.Copy(&to, from) newValue := "new value" to.Detail.Info2 = &newValue if to.Detail.Info1 == "" { t.Errorf("should not be empty") } if to.Detail.Info1 != from.Detail.Info1 { t.Errorf("should be the same") } if to.Detail.Info2 == from.Detail.Info2 { t.Errorf("should be different") } }) t.Run("Should work with from a nil ptr slice field to a slice field", func(t *testing.T) { notes := []string{"hello", "world"} from := UserWithDetailsPtr{Notes: ¬es, Notes2: nil} to := UserWithDetails{} err := copier.Copy(&to, from) if err != nil { t.Errorf("should not return an error") return } if len(to.Notes) != len(*from.Notes) { t.Errorf("should be the same length") } if to.Notes[0] != (*from.Notes)[0] { t.Errorf("should be the same") } if to.Notes[1] != (*from.Notes)[1] { t.Errorf("should be the same") } }) }) t.Run("Should work with deepCopy", func(t *testing.T) { t.Run("Should work with same type and both ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetailsPtr{ Detail: &Detail{Info1: "hello", Info2: &info2}, Details: []*Detail{{Info1: "hello", Info2: &info2}}, } to := UserWithDetailsPtr{} copier.CopyWithOption(&to, from, optionsDeepCopy) checkDetail(t, *from.Detail, *to.Detail) *to.Detail.Info2 = "new value" if *from.Detail.Info2 == *to.Detail.Info2 { t.Fatalf("DeepCopy enabled") } if len(from.Details) != len(to.Details) { t.Fatalf("slice should be copied") } for idx, detail := range from.Details { checkDetail(t, *detail, *to.Details[idx]) } }) t.Run("Should work with same type and both not ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetails{ Detail: Detail{Info1: "hello", Info2: &info2}, Details: []Detail{{Info1: "hello", Info2: &info2}}, } to := UserWithDetails{} copier.CopyWithOption(&to, from, optionsDeepCopy) checkDetail(t, from.Detail, to.Detail) *to.Detail.Info2 = "new value" if *from.Detail.Info2 == *to.Detail.Info2 { t.Fatalf("DeepCopy enabled") } if len(from.Details) != len(to.Details) { t.Fatalf("slice should be copied") } for idx, detail := range from.Details { checkDetail(t, detail, to.Details[idx]) } }) t.Run("Should work with different type and both ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetailsPtr{Detail: &Detail{Info1: "hello", Info2: &info2}} to := EmployeeWithDetailsPtr{} copier.CopyWithOption(&to, from, optionsDeepCopy) newValue := "new value" to.Detail.Info2 = &newValue if to.Detail.Info1 == "" { t.Errorf("should not be empty") } if to.Detail.Info1 != from.Detail.Info1 { t.Errorf("should be the same") } if to.Detail.Info2 == from.Detail.Info2 { t.Errorf("should be different") } }) t.Run("Should work with different type and both not ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetails{Detail: Detail{Info1: "hello", Info2: &info2}} to := EmployeeWithDetails{} copier.CopyWithOption(&to, from, optionsDeepCopy) newValue := "new value" to.Detail.Info2 = &newValue if to.Detail.Info1 == "" { t.Errorf("should not be empty") } if to.Detail.Info1 != from.Detail.Info1 { t.Errorf("should be the same") } if to.Detail.Info2 == from.Detail.Info2 { t.Errorf("should be different") } }) t.Run("Should work with from ptr field and to not ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetailsPtr{Detail: &Detail{Info1: "hello", Info2: &info2}} to := EmployeeWithDetails{} copier.CopyWithOption(&to, from, optionsDeepCopy) newValue := "new value" to.Detail.Info2 = &newValue if to.Detail.Info1 == "" { t.Errorf("should not be empty") } if to.Detail.Info1 != from.Detail.Info1 { t.Errorf("should be the same") } if to.Detail.Info2 == from.Detail.Info2 { t.Errorf("should be different") } }) t.Run("Should work with from not ptr field and to ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetails{Detail: Detail{Info1: "hello", Info2: &info2}} to := EmployeeWithDetailsPtr{} copier.CopyWithOption(&to, from, optionsDeepCopy) newValue := "new value" to.Detail.Info2 = &newValue if to.Detail.Info1 == "" { t.Errorf("should not be empty") } if to.Detail.Info1 != from.Detail.Info1 { t.Errorf("should be the same") } if to.Detail.Info2 == from.Detail.Info2 { t.Errorf("should be different") } }) t.Run("Should work with from a nil ptr slice field to a slice field", func(t *testing.T) { notes := []string{"hello", "world"} from := UserWithDetailsPtr{Notes: ¬es, Notes2: nil} to := UserWithDetails{} err := copier.CopyWithOption(&to, from, optionsDeepCopy) if err != nil { t.Errorf("should not return an error") return } if len(to.Notes) != len(*from.Notes) { t.Errorf("should be the same length") } if to.Notes[0] != (*from.Notes)[0] { t.Errorf("should be the same") } if to.Notes[1] != (*from.Notes)[1] { t.Errorf("should be the same") } newValue := []string{"new", "value"} to.Notes = newValue if to.Notes[0] == (*from.Notes)[0] { t.Errorf("should be different") } if to.Notes[1] == (*from.Notes)[1] { t.Errorf("should be different") } }) }) } func TestMapInterface(t *testing.T) { type Inner struct { IntPtr *int unexportedField string } type Outer struct { Inner Inner } type DriverOptions struct { GenOptions map[string]interface{} } t.Run("Should work without deepCopy", func(t *testing.T) { intVal := 5 outer := Outer{ Inner: Inner{ IntPtr: &intVal, unexportedField: "hello", }, } from := DriverOptions{ GenOptions: map[string]interface{}{ "key": outer, }, } to := DriverOptions{} if err := copier.Copy(&to, &from); nil != err { t.Errorf("Unexpected error: %v", err) return } *to.GenOptions["key"].(Outer).Inner.IntPtr = 6 if to.GenOptions["key"].(Outer).Inner.IntPtr != from.GenOptions["key"].(Outer).Inner.IntPtr { t.Errorf("should be the same") } }) t.Run("Should work with deepCopy", func(t *testing.T) { intVal := 5 outer := Outer{ Inner: Inner{ IntPtr: &intVal, unexportedField: "Hello", }, } from := DriverOptions{ GenOptions: map[string]interface{}{ "key": outer, }, } to := DriverOptions{} if err := copier.CopyWithOption(&to, &from, copier.Option{ DeepCopy: true, }); nil != err { t.Errorf("Unexpected error: %v", err) return } *to.GenOptions["key"].(Outer).Inner.IntPtr = 6 if to.GenOptions["key"].(Outer).Inner.IntPtr == from.GenOptions["key"].(Outer).Inner.IntPtr { t.Errorf("should be different") } }) t.Run("Test copy map with nil interface", func(t *testing.T) { from := map[string]interface{}{"eventId": nil} to := map[string]interface{}{"eventId": nil} copier.CopyWithOption(&to, &from, copier.Option{IgnoreEmpty: true, DeepCopy: true}) if v, ok := to["eventId"]; !ok || v != nil { t.Errorf("failed to deep copy map with nil, got %v", v) } from["eventId"] = 1 if v, ok := to["eventId"]; !ok || v != nil { t.Errorf("failed to deep copy map with nil, got %v", v) } copier.CopyWithOption(&to, &from, copier.Option{IgnoreEmpty: true, DeepCopy: true}) if v, ok := to["eventId"]; !ok || v != 1 { t.Errorf("failed to deep copy map with nil") } from["eventId"] = 2 if v, ok := to["eventId"]; !ok || v != 1 { t.Errorf("failed to deep copy map with nil") } }) t.Run("Test copy map with nested slice map", func(t *testing.T) { var out map[string]interface{} value := map[string]interface{}{ "list": []map[string]interface{}{ { "shop_id": 123, }, }, "list2": []interface{}{ map[string]interface{}{ "shop_id": 123, }, }, } err := copier.CopyWithOption(&out, &value, copier.Option{IgnoreEmpty: false, DeepCopy: true}) if err != nil { t.Fatalf("failed to deep copy nested map") } if fmt.Sprintf("%v", out) != fmt.Sprintf("%v", value) { t.Fatalf("failed to deep copy nested map") } }) } func TestInterface(t *testing.T) { type Inner struct { IntPtr *int } type Outer struct { Inner Inner } type DriverOptions struct { GenOptions interface{} } t.Run("Should work without deepCopy", func(t *testing.T) { intVal := 5 outer := Outer{ Inner: Inner{ IntPtr: &intVal, }, } from := DriverOptions{ GenOptions: outer, } to := DriverOptions{} if err := copier.Copy(&to, from); nil != err { t.Errorf("Unexpected error: %v", err) return } *to.GenOptions.(Outer).Inner.IntPtr = 6 if to.GenOptions.(Outer).Inner.IntPtr != from.GenOptions.(Outer).Inner.IntPtr { t.Errorf("should be the same") } }) t.Run("Should work with deepCopy", func(t *testing.T) { intVal := 5 outer := Outer{ Inner: Inner{ IntPtr: &intVal, }, } from := DriverOptions{ GenOptions: outer, } to := DriverOptions{} if err := copier.CopyWithOption(&to, &from, copier.Option{ DeepCopy: true, }); nil != err { t.Errorf("Unexpected error: %v", err) return } *to.GenOptions.(Outer).Inner.IntPtr = 6 if to.GenOptions.(Outer).Inner.IntPtr == from.GenOptions.(Outer).Inner.IntPtr { t.Errorf("should be different") } }) } func TestSlice(t *testing.T) { type ElemOption struct { Value int } type A struct { X []int Options []ElemOption } type B struct { X []int Options []ElemOption } t.Run("Should work with simple slice", func(t *testing.T) { from := []int{1, 2} var to []int if err := copier.Copy(&to, from); nil != err { t.Errorf("Unexpected error: %v", err) return } from[0] = 3 from[1] = 4 if to[0] == from[0] { t.Errorf("should be different") } if len(to) != len(from) { t.Errorf("should be the same length, got len(from): %v, len(to): %v", len(from), len(to)) } }) t.Run("Should work with empty slice", func(t *testing.T) { from := []int{} to := []int{} if err := copier.Copy(&to, from); nil != err { t.Errorf("Unexpected error: %v", err) return } if to == nil { t.Errorf("should be not nil") } }) t.Run("Should work without deepCopy", func(t *testing.T) { x := []int{1, 2} options := []ElemOption{ {Value: 10}, {Value: 20}, } from := A{X: x, Options: options} to := B{} if err := copier.Copy(&to, from); nil != err { t.Errorf("Unexpected error: %v", err) return } from.X[0] = 3 from.X[1] = 4 from.Options[0].Value = 30 from.Options[1].Value = 40 if to.X[0] != from.X[0] { t.Errorf("should be the same") } if len(to.X) != len(from.X) { t.Errorf("should be the same length, got len(from.X): %v, len(to.X): %v", len(from.X), len(to.X)) } if to.Options[0].Value != from.Options[0].Value { t.Errorf("should be the same") } if to.Options[0].Value != from.Options[0].Value { t.Errorf("should be the same") } if len(to.Options) != len(from.Options) { t.Errorf("should be the same") } }) t.Run("Should work with deepCopy", func(t *testing.T) { x := []int{1, 2} options := []ElemOption{ {Value: 10}, {Value: 20}, } from := A{X: x, Options: options} to := B{} if err := copier.CopyWithOption(&to, from, copier.Option{ DeepCopy: true, }); nil != err { t.Errorf("Unexpected error: %v", err) return } from.X[0] = 3 from.X[1] = 4 from.Options[0].Value = 30 from.Options[1].Value = 40 if to.X[0] == from.X[0] { t.Errorf("should be different") } if len(to.X) != len(from.X) { t.Errorf("should be the same length, got len(from.X): %v, len(to.X): %v", len(from.X), len(to.X)) } if to.Options[0].Value == from.Options[0].Value { t.Errorf("should be different") } if len(to.Options) != len(from.Options) { t.Errorf("should be the same") } }) } func TestAnonymousFields(t *testing.T) { t.Run("Should work with unexported ptr fields", func(t *testing.T) { type nested struct { A string } type parentA struct { *nested } type parentB struct { *nested } from := parentA{nested: &nested{A: "a"}} to := parentB{} err := copier.CopyWithOption(&to, &from, copier.Option{ DeepCopy: true, }) if err != nil { t.Errorf("Unexpected error: %v", err) return } from.nested.A = "b" if to.nested != nil { t.Errorf("should be nil") } }) t.Run("Should work with unexported fields", func(t *testing.T) { type nested struct { A string } type parentA struct { nested } type parentB struct { nested } from := parentA{nested: nested{A: "a"}} to := parentB{} err := copier.CopyWithOption(&to, &from, copier.Option{ DeepCopy: true, }) if err != nil { t.Errorf("Unexpected error: %v", err) return } from.nested.A = "b" if to.nested.A == from.nested.A { t.Errorf("should be different") } }) t.Run("Should work with exported ptr fields", func(t *testing.T) { type Nested struct { A string } type parentA struct { *Nested } type parentB struct { *Nested } fieldValue := "a" from := parentA{Nested: &Nested{A: fieldValue}} to := parentB{} err := copier.CopyWithOption(&to, &from, copier.Option{ DeepCopy: true, }) if err != nil { t.Errorf("Unexpected error: %v", err) return } from.Nested.A = "b" if to.Nested.A != fieldValue { t.Errorf("should not change") } }) t.Run("Should work with exported ptr fields with same name src field", func(t *testing.T) { type Nested struct { A string } type parentA struct { A string } type parentB struct { *Nested } fieldValue := "a" from := parentA{A: fieldValue} to := parentB{} err := copier.CopyWithOption(&to, &from, copier.Option{ DeepCopy: true, }) if err != nil { t.Errorf("Unexpected error: %v", err) return } from.A = "b" if to.Nested.A != fieldValue { t.Errorf("should not change") } }) t.Run("Should work with exported fields", func(t *testing.T) { type Nested struct { A string } type parentA struct { Nested } type parentB struct { Nested } fieldValue := "a" from := parentA{Nested: Nested{A: fieldValue}} to := parentB{} err := copier.CopyWithOption(&to, &from, copier.Option{ DeepCopy: true, }) if err != nil { t.Errorf("Unexpected error: %v", err) return } from.Nested.A = "b" if to.Nested.A != fieldValue { t.Errorf("should not change") } }) } type someStruct struct { IntField int UIntField uint64 } type structSameName1 struct { A string B int64 C time.Time D string E *someStruct } type structSameName2 struct { A string B time.Time C int64 D string E *someStruct } func TestCopyFieldsWithSameNameButDifferentTypes(t *testing.T) { obj1 := structSameName1{A: "123", B: 2, C: time.Now()} obj2 := &structSameName2{} err := copier.Copy(obj2, &obj1) if err != nil { t.Error("Should not raise error") } if obj2.A != obj1.A { t.Errorf("Field A should be copied") } } type Foo1 struct { Name string Age int32 } type Foo2 struct { Name string } type StructWithMap1 struct { Map map[int]Foo1 } type StructWithMap2 struct { Map map[int32]Foo2 } func TestCopyMapOfStruct(t *testing.T) { obj1 := StructWithMap1{Map: map[int]Foo1{2: {Name: "A pure foo"}}} obj2 := &StructWithMap2{} err := copier.Copy(obj2, obj1) if err != nil { t.Error("Should not raise error") } for k, v1 := range obj1.Map { v2, ok := obj2.Map[int32(k)] if !ok || v1.Name != v2.Name { t.Errorf("Map should be copied") } } } func TestCopyMapOfInt(t *testing.T) { map1 := map[int]int{3: 6, 4: 8} map2 := map[int32]int8{} err := copier.Copy(&map2, map1) if err != nil { t.Error("Should not raise error") } for k, v1 := range map1 { v2, ok := map2[int32(k)] if !ok || v1 != int(v2) { t.Errorf("Map should be copied") } } } func TestCopyMapOfSliceValue(t *testing.T) { // case1: map's value is a simple slice key, value := 2, 3 src := map[int][]int{key: {value}} dst1 := map[int][]int{} var dst2 map[int][]int err := copier.Copy(&dst1, src) if err != nil { t.Error("Should not raise error") } err = copier.Copy(&dst2, src) if err != nil { t.Error("Should not raise error") } for k, v1 := range src { v2, ok := dst1[k] if !ok || len(v1) != len(v2) || k != key { t.Errorf("Map should be copied") } for i := range v1 { if v2[i] != value { t.Errorf("Map's slice value shoud be copied") } } v3, ok := dst2[k] if !ok || len(v1) != len(v3) { t.Errorf("Map should be copied") } for i := range v1 { if v3[i] != value { t.Errorf("Map's slice value shoud be copied") } } } // case2: map's value is a slice whose element is map key1, key2 := 2, 3 value = 4 s := map[int][]map[int]int{key1: {{key2: value}}} d1 := map[int][]map[int]int{key1: {{key1: key2}}} d2 := map[int][]map[int]int{key1: {}} d3 := map[int][]map[int]int{key1: nil} d4 := map[int][]map[int]int{} d5 := map[int][]map[int]int(nil) ms := []map[int][]map[int]int{d1, d2, d3, d4, d5} for i := range ms { copier.CopyWithOption(&ms[i], s, copier.Option{IgnoreEmpty: false, DeepCopy: true}) if len(ms[i]) != len(s) { t.Errorf("Number of map's keys should be equal") } for k, sliceMap := range ms[i] { if k != key1 { t.Errorf("Map's key should be copied") } if len(sliceMap) != len(s[key1]) || len(sliceMap) != 1 { t.Errorf("Map's slice value should be copied") } m := sliceMap[0] if len(m) != len(s[key1][0]) || len(m) != 1 { t.Errorf("Map's slice value should be copied recursively") } for k, v := range m { if k != key2 || v != value { t.Errorf("Map's slice value should be copied recursively") } } } } } func TestCopyMapOfPtrValue(t *testing.T) { intV := 3 intv := intV src := map[int]*int{2: &intv} dst1 := map[int]*int{} var dst2 map[int]*int err := copier.Copy(&dst1, src) if err != nil { t.Error("Should not raise error") } err = copier.Copy(&dst2, src) if err != nil { t.Error("Should not raise error") } for k, v1 := range src { v2, ok := dst1[k] if !ok || v2 == nil || v1 == nil || *v2 != *v1 || *v2 != intV { t.Errorf("Map should be copied") } v3, ok := dst2[k] if !ok || v3 == nil || *v3 != *v1 || *v3 != intV { t.Errorf("Map should be copied") } } } func TestCopyWithOption(t *testing.T) { from := structSameName2{D: "456", E: &someStruct{IntField: 100, UIntField: 1000}} to := &structSameName1{A: "123", B: 2, C: time.Now(), D: "123", E: &someStruct{UIntField: 5000}} if err := copier.CopyWithOption(to, &from, copier.Option{IgnoreEmpty: true}); err != nil { t.Error("Should not raise error") } if to.A == from.A { t.Errorf("Field A should not be copied") } else if to.D != from.D { t.Errorf("Field D should be copied") } } type ScannerValue struct { V int } func (s *ScannerValue) Scan(src interface{}) error { return errors.New("I failed") } type ScannerStruct struct { V *ScannerValue } type ScannerStructTo struct { V *ScannerValue } func TestScanner(t *testing.T) { s := &ScannerStruct{ V: &ScannerValue{ V: 12, }, } s2 := &ScannerStructTo{} err := copier.Copy(s2, s) if err != nil { t.Error("Should not raise error") } if s.V.V != s2.V.V { t.Errorf("Field V should be copied") } } func TestScanFromPtrToSqlNullable(t *testing.T) { var ( from struct { S string Sptr *string Snull sql.NullString T1 sql.NullTime T2 sql.NullTime T3 *time.Time } to struct { S sql.NullString Sptr sql.NullString Snull *string T1 time.Time T2 *time.Time T3 sql.NullTime } s string err error ) s = "test" from.S = s from.Sptr = &s if from.T1.Valid || from.T2.Valid { t.Errorf("Must be not valid") } err = copier.Copy(&to, from) if err != nil { t.Error("Should not raise error") } if !to.T1.IsZero() { t.Errorf("to.T1 should be Zero but %v", to.T1) } if to.T2 != nil { t.Errorf("to.T2 should be nil but %v", to.T2) } if to.Snull != nil { t.Errorf("to.Snull should be nil but %v", to.Snull) } now := time.Now() from.T1.Scan(now) from.T2.Scan(now) err = copier.Copy(&to, from) if err != nil { t.Error("Should not raise error") } if to.S.String != from.S { t.Errorf("Field S should be copied") } if to.Sptr.String != *from.Sptr { t.Errorf("Field Sptr should be copied") } if from.T1.Time != to.T1 { t.Errorf("Fields T1 fields should be equal") } if from.T2.Time != *to.T2 { t.Errorf("Fields T2 fields should be equal") } } func TestDeepCopyInterface(t *testing.T) { m := make(map[string]string) m["a"] = "ccc" from := []interface{}{[]int{7, 8, 9}, 2, 3, m, errors.New("aaaa")} var to []interface{} copier.CopyWithOption(&to, &from, copier.Option{ IgnoreEmpty: false, DeepCopy: true, }) from[0].([]int)[0] = 10 from[1] = "3" from[3].(map[string]string)["a"] = "bbb" if fmt.Sprint(to[0]) != fmt.Sprint([]int{7, 8, 9}) { t.Errorf("to value failed to be deep copied") } if fmt.Sprint(to[1]) != "2" { t.Errorf("to value failed to be deep copied") } if to[3].(map[string]string)["a"] != "ccc" { t.Errorf("to value failed to be deep copied") } } func TestDeepCopyTime(t *testing.T) { type embedT1 struct { T5 time.Time } type embedT2 struct { T6 *time.Time } var ( from struct { T1 time.Time T2 *time.Time T3 *time.Time T4 time.Time T5 time.Time T6 time.Time } to struct { T1 time.Time T2 *time.Time T3 time.Time T4 *time.Time embedT1 embedT2 } ) t1 := time.Now() from.T1 = t1 t2 := t1.Add(time.Second) from.T2 = &t2 t3 := t2.Add(time.Second) from.T3 = &t3 t4 := t3.Add(time.Second) from.T4 = t4 t5 := t4.Add(time.Second) from.T5 = t5 t6 := t5.Add(time.Second) from.T6 = t6 err := copier.CopyWithOption(&to, from, copier.Option{DeepCopy: true}) if err != nil { t.Error("Should not raise error") } if !to.T1.Equal(from.T1) { t.Errorf("Field T1 should be copied") } if !to.T2.Equal(*from.T2) { t.Errorf("Field T2 should be copied") } if !to.T3.Equal(*from.T3) { t.Errorf("Field T3 should be copied") } if !to.T4.Equal(from.T4) { t.Errorf("Field T4 should be copied") } if !to.T5.Equal(from.T5) { t.Errorf("Field T5 should be copied") } if !to.T6.Equal(from.T6) { t.Errorf("Field T6 should be copied") } } func TestNestedPrivateData(t *testing.T) { type hasPrivate struct { data int } type hasMembers struct { Member hasPrivate } src := hasMembers{ Member: hasPrivate{ data: 42, }, } var shallow hasMembers err := copier.Copy(&shallow, &src) if err != nil { t.Errorf("could not complete shallow copy") } if !reflect.DeepEqual(&src, &shallow) { t.Errorf("shallow copy faild") } var deep hasMembers err = copier.CopyWithOption(&deep, &src, copier.Option{DeepCopy: true}) if err != nil { t.Errorf("could not complete deep copy") } if !reflect.DeepEqual(&src, &deep) { t.Errorf("deep copy faild") } if !reflect.DeepEqual(&shallow, &deep) { t.Errorf("unexpected difference between shallow and deep copy") } } func TestDeepMapCopyTime(t *testing.T) { t1 := time.Now() t2 := t1.Add(time.Second) from := []map[string]interface{}{ { "t1": t1, "t2": &t2, }, } to := make([]map[string]interface{}, len(from)) err := copier.CopyWithOption(&to, from, copier.Option{DeepCopy: true}) if err != nil { t.Error("should not error") } if len(to) != len(from) { t.Errorf("slice should be copied") } if !to[0]["t1"].(time.Time).Equal(from[0]["t1"].(time.Time)) { t.Errorf("nested time ptr should be copied") } if !to[0]["t2"].(*time.Time).Equal(*from[0]["t2"].(*time.Time)) { t.Errorf("nested time ptr should be copied") } } func TestCopySimpleTime(t *testing.T) { from := time.Now() to := time.Time{} err := copier.Copy(&to, from) if err != nil { t.Error("should not error") } if !from.Equal(to) { t.Errorf("to (%v) value should equal from (%v) value", to, from) } } func TestDeepCopySimpleTime(t *testing.T) { from := time.Now() to := time.Time{} err := copier.CopyWithOption(&to, from, copier.Option{DeepCopy: true}) if err != nil { t.Error("should not error") } if !from.Equal(to) { t.Errorf("to (%v) value should equal from (%v) value", to, from) } } type TimeWrapper struct { time.Time } func TestDeepCopyAnonymousFieldTime(t *testing.T) { from := TimeWrapper{time.Now()} to := TimeWrapper{} err := copier.CopyWithOption(&to, from, copier.Option{DeepCopy: true}) if err != nil { t.Error("should not error") } if !from.Time.Equal(to.Time) { t.Errorf("to (%v) value should equal from (%v) value", to.Time, from.Time) } } func TestSqlNullFiled(t *testing.T) { type sqlStruct struct { MkId sql.NullInt64 MkExpiryDateType sql.NullInt32 MkExpiryDateStart sql.NullString } type dataStruct struct { MkId int64 MkExpiryDateType int32 MkExpiryDateStart string } from := sqlStruct{ MkId: sql.NullInt64{Int64: 3, Valid: true}, MkExpiryDateType: sql.NullInt32{Int32: 4, Valid: true}, MkExpiryDateStart: sql.NullString{String: "5", Valid: true}, } to := dataStruct{} err := copier.Copy(&to, from) if err != nil { t.Error("should not error") } if from.MkId.Int64 != to.MkId { t.Errorf("to (%v) value should equal from (%v) value", to.MkId, from.MkId.Int64) } if from.MkExpiryDateStart.String != to.MkExpiryDateStart { t.Errorf("to (%v) value should equal from (%v) value", to.MkExpiryDateStart, from.MkExpiryDateStart.String) } if from.MkExpiryDateType.Int32 != to.MkExpiryDateType { t.Errorf("to (%v) value should equal from (%v) value", to.MkExpiryDateType, from.MkExpiryDateType.Int32) } } func TestEmptySlice(t *testing.T) { type Str1 string type Str2 string type Input1 struct { Val Str1 } type Input2 struct { Val Str2 } to := []*Input1(nil) from := []*Input2{} err := copier.Copy(&to, &from) if err != nil { t.Error("should not error") } if from == nil { t.Error("from should be empty slice not nil") } to = []*Input1(nil) from = []*Input2(nil) err = copier.Copy(&to, &from) if err != nil { t.Error("should not error") } if from != nil { t.Error("from should be empty slice nil") } } func TestNestedNilPointerStruct(t *testing.T) { type destination struct { Title string } type NestedSource struct { ID int } type source struct { Title string *NestedSource } from := &source{ Title: "A title to be copied", } to := destination{} err := copier.Copy(&to, from) if err != nil { t.Error("should not error") } if from.Title != to.Title { t.Errorf("to (%v) value should equal from (%v) value", to.Title, from.Title) } }