package deepcopy import ( "fmt" "reflect" "testing" "time" "unsafe" "github.com/stretchr/testify/assert" ) // hasSameDataPointer 检查两个切片是否共享相同的底层数组 func hasSameDataPointer(a1, b1 interface{}) bool { a := reflect.ValueOf(a1) b := reflect.ValueOf(b1) // 首先确保两个值的类型相同 if a.Kind() != b.Kind() { return false } switch a.Kind() { case reflect.Slice: // 对于切片,比较底层数组指针 return unsafe.Pointer(a.Pointer()) == unsafe.Pointer(b.Pointer()) case reflect.Ptr: // 对于指针,直接比较指针值 return a.Pointer() == b.Pointer() case reflect.Map: // 对于映射,比较映射指针 return a.Pointer() == b.Pointer() case reflect.Chan: // 对于通道,比较通道指针 return a.Pointer() == b.Pointer() default: // 其他类型可能不支持直接的内存比较 return false } // aVal := reflect.ValueOf(a) // bVal := reflect.ValueOf(b) // if aVal.Kind() != reflect.Slice || bVal.Kind() != reflect.Slice { // return false // } // a1 := (*reflect.SliceHeader)(unsafe.Pointer(aVal.Pointer())) // b1 := (*reflect.SliceHeader)(unsafe.Pointer(bVal.Pointer())) // return a1.Data == b1.Data } // just basic is this working stuff func TestSimple(t *testing.T) { t.Run("Copy[]string", func(t *testing.T) { original := []string{"a", "b", "c"} originalC := []string{"a", "b", "d"} if hasSameDataPointer(original, originalC) { fmt.Println("same") } copied, err := Copy(original) assert.NoError(t, err) cpyS := copied.([]string) assert.False(t, hasSameDataPointer(original, cpyS), "expected different memory addresses") assert.Equal(t, len(original), len(cpyS), "length should match") assert.Equal(t, original, cpyS, "content should match") }) t.Run("Copy[]bool", func(t *testing.T) { original := []bool{true, true, false, false} copied, err := Copy(original) assert.NoError(t, err) cpyB := copied.([]bool) assert.False(t, hasSameDataPointer(original, cpyB), "expected different memory addresses") assert.Equal(t, len(original), len(cpyB), "length should match") assert.Equal(t, original, cpyB, "content should match") }) t.Run("Copy[]byte", func(t *testing.T) { original := []byte("hello") copied, err := Copy(original) assert.NoError(t, err) cpyBt := copied.([]byte) assert.False(t, hasSameDataPointer(original, cpyBt), "expected different memory addresses") assert.Equal(t, len(original), len(cpyBt), "length should match") assert.Equal(t, original, cpyBt, "content should match") }) t.Run("Copy[]int", func(t *testing.T) { original := []int{42} copied, err := Copy(original) assert.NoError(t, err) cpyI := copied.([]int) assert.False(t, hasSameDataPointer(original, cpyI), "expected different memory addresses") assert.Equal(t, len(original), len(cpyI), "length should match") assert.Equal(t, original, cpyI, "content should match") }) t.Run("Copy[]uint", func(t *testing.T) { original := []uint{1, 2, 3, 4, 5} copied, err := Copy(original) assert.NoError(t, err) cpyU := copied.([]uint) assert.False(t, hasSameDataPointer(original, cpyU), "expected different memory addresses") assert.Equal(t, len(original), len(cpyU), "length should match") assert.Equal(t, original, cpyU, "content should match") }) t.Run("Copy[]float32", func(t *testing.T) { original := []float32{3.14} copied, err := Copy(original) assert.NoError(t, err) cpyF := copied.([]float32) assert.False(t, hasSameDataPointer(original, cpyF), "expected different memory addresses") assert.Equal(t, len(original), len(cpyF), "length should match") assert.Equal(t, original, cpyF, "content should match") }) t.Run("Copy[]interface{}", func(t *testing.T) { original := []interface{}{"a", 42, true, 4.32} copied, err := Copy(original) assert.NoError(t, err) cpyIf := copied.([]interface{}) assert.False(t, hasSameDataPointer(original, cpyIf), "expected different memory addresses") assert.Equal(t, len(original), len(cpyIf), "length should match") assert.Equal(t, original, cpyIf, "content should match") }) } type Basics struct { String string Strings []string StringArr [4]string Bool bool Bools []bool Byte byte Bytes []byte Int int Ints []int Int8 int8 Int8s []int8 Int16 int16 Int16s []int16 Int32 int32 Int32s []int32 Int64 int64 Int64s []int64 Uint uint Uints []uint Uint8 uint8 Uint8s []uint8 Uint16 uint16 Uint16s []uint16 Uint32 uint32 Uint32s []uint32 Uint64 uint64 Uint64s []uint64 Float32 float32 Float32s []float32 Float64 float64 Float64s []float64 Complex64 complex64 Complex64s []complex64 Complex128 complex128 Complex128s []complex128 Interface interface{} Interfaces []interface{} } // These tests test that all supported basic types are copied correctly. This // is done by copying a struct with fields of most of the basic types as []T. func TestMostTypes(t *testing.T) { test := Basics{ String: "kimchi", Strings: []string{"uni", "ika"}, StringArr: [4]string{"malort", "barenjager", "fernet", "salmiakki"}, Bool: true, Bools: []bool{true, false, true}, Byte: 'z', Bytes: []byte("abc"), Int: 42, Ints: []int{0, 1, 3, 4}, Int8: 8, Int8s: []int8{8, 9, 10}, Int16: 16, Int16s: []int16{16, 17, 18, 19}, Int32: 32, Int32s: []int32{32, 33}, Int64: 64, Int64s: []int64{64}, Uint: 420, Uints: []uint{11, 12, 13}, Uint8: 81, Uint8s: []uint8{81, 82}, Uint16: 160, Uint16s: []uint16{160, 161, 162, 163, 164}, Uint32: 320, Uint32s: []uint32{320, 321}, Uint64: 640, Uint64s: []uint64{6400, 6401, 6402, 6403}, Float32: 32.32, Float32s: []float32{32.32, 33}, Float64: 64.1, Float64s: []float64{64, 65, 66}, Complex64: complex64(-64 + 12i), Complex64s: []complex64{complex64(-65 + 11i), complex64(66 + 10i)}, Complex128: complex128(-128 + 12i), Complex128s: []complex128{complex128(-128 + 11i), complex128(129 + 10i)}, Interfaces: []interface{}{42, true, "pan-galactic"}, } cpy, err := Copy(test) if err != nil { t.Fatal(err) } cpyBasics := cpy.(Basics) // see if they point to the same location if fmt.Sprintf("%p", &cpyBasics) == fmt.Sprintf("%p", &test) { t.Error("address of copy was the same as original; they should be different") return } // Go through each field and check to see it got copied properly if cpyBasics.String != test.String { t.Errorf("String: got %v; want %v", cpyBasics.String, test.String) } if hasSameDataPointer(test.Strings, cpyBasics.Strings) { t.Error("Strings: address of copy was the same as original; they should be different") goto StringArr } if len(cpyBasics.Strings) != len(test.Strings) { t.Errorf("Strings: len was %d; want %d", len(cpyBasics.Strings), len(test.Strings)) goto StringArr } for i, v := range test.Strings { if v != cpyBasics.Strings[i] { t.Errorf("Strings: got %v at index %d of the copy; want %v", cpyBasics.Strings[i], i, v) } } StringArr: if unsafe.Pointer(&test.StringArr) == unsafe.Pointer(&cpyBasics.StringArr) { t.Error("StringArr: address of copy was the same as original; they should be different") goto Bools } for i, v := range test.StringArr { if v != cpyBasics.StringArr[i] { t.Errorf("StringArr: got %v at index %d of the copy; want %v", cpyBasics.StringArr[i], i, v) } } Bools: if cpyBasics.Bool != test.Bool { t.Errorf("Bool: got %v; want %v", cpyBasics.Bool, test.Bool) } if hasSameDataPointer(test.Bools, cpyBasics.Bools) { t.Error("Bools: address of copy was the same as original; they should be different") goto Bytes } if len(cpyBasics.Bools) != len(test.Bools) { t.Errorf("Bools: len was %d; want %d", len(cpyBasics.Bools), len(test.Bools)) goto Bytes } for i, v := range test.Bools { if v != cpyBasics.Bools[i] { t.Errorf("Bools: got %v at index %d of the copy; want %v", cpyBasics.Bools[i], i, v) } } Bytes: if cpyBasics.Byte != test.Byte { t.Errorf("Byte: got %v; want %v", cpyBasics.Byte, test.Byte) } if hasSameDataPointer(test.Bytes, cpyBasics.Bytes) { t.Error("Bytes: address of copy was the same as original; they should be different") goto Ints } if len(cpyBasics.Bytes) != len(test.Bytes) { t.Errorf("Bytes: len was %d; want %d", len(cpyBasics.Bytes), len(test.Bytes)) goto Ints } for i, v := range test.Bytes { if v != cpyBasics.Bytes[i] { t.Errorf("Bytes: got %v at index %d of the copy; want %v", cpyBasics.Bytes[i], i, v) } } Ints: if cpyBasics.Int != test.Int { t.Errorf("Int: got %v; want %v", cpyBasics.Int, test.Int) } if hasSameDataPointer(test.Ints, cpyBasics.Ints) { t.Error("Ints: address of copy was the same as original; they should be different") goto Int8s } if len(cpyBasics.Ints) != len(test.Ints) { t.Errorf("Ints: len was %d; want %d", len(cpyBasics.Ints), len(test.Ints)) goto Int8s } for i, v := range test.Ints { if v != cpyBasics.Ints[i] { t.Errorf("Ints: got %v at index %d of the copy; want %v", cpyBasics.Ints[i], i, v) } } Int8s: if cpyBasics.Int8 != test.Int8 { t.Errorf("Int8: got %v; want %v", cpyBasics.Int8, test.Int8) } if hasSameDataPointer(test.Int8s, cpyBasics.Int8s) { t.Error("Int8s: address of copy was the same as original; they should be different") goto Int16s } if len(cpyBasics.Int8s) != len(test.Int8s) { t.Errorf("Int8s: len was %d; want %d", len(cpyBasics.Int8s), len(test.Int8s)) goto Int16s } for i, v := range test.Int8s { if v != cpyBasics.Int8s[i] { t.Errorf("Int8s: got %v at index %d of the copy; want %v", cpyBasics.Int8s[i], i, v) } } Int16s: if cpyBasics.Int16 != test.Int16 { t.Errorf("Int16: got %v; want %v", cpyBasics.Int16, test.Int16) } if hasSameDataPointer(test.Int16s, cpyBasics.Int16s) { t.Error("Int16s: address of copy was the same as original; they should be different") goto Int32s } if len(cpyBasics.Int16s) != len(test.Int16s) { t.Errorf("Int16s: len was %d; want %d", len(cpyBasics.Int16s), len(test.Int16s)) goto Int32s } for i, v := range test.Int16s { if v != cpyBasics.Int16s[i] { t.Errorf("Int16s: got %v at index %d of the copy; want %v", cpyBasics.Int16s[i], i, v) } } Int32s: if cpyBasics.Int32 != test.Int32 { t.Errorf("Int32: got %v; want %v", cpyBasics.Int32, test.Int32) } if hasSameDataPointer(test.Int32s, cpyBasics.Int32s) { t.Error("Int32s: address of copy was the same as original; they should be different") goto Int64s } if len(cpyBasics.Int32s) != len(test.Int32s) { t.Errorf("Int32s: len was %d; want %d", len(cpyBasics.Int32s), len(test.Int32s)) goto Int64s } for i, v := range test.Int32s { if v != cpyBasics.Int32s[i] { t.Errorf("Int32s: got %v at index %d of the copy; want %v", cpyBasics.Int32s[i], i, v) } } Int64s: if cpyBasics.Int64 != test.Int64 { t.Errorf("Int64: got %v; want %v", cpyBasics.Int64, test.Int64) } if hasSameDataPointer(test.Int64s, cpyBasics.Int64s) { t.Error("Int64s: address of copy was the same as original; they should be different") goto Uints } if len(cpyBasics.Int64s) != len(test.Int64s) { t.Errorf("Int64s: len was %d; want %d", len(cpyBasics.Int64s), len(test.Int64s)) goto Uints } for i, v := range test.Int64s { if v != cpyBasics.Int64s[i] { t.Errorf("Int64s: got %v at index %d of the copy; want %v", cpyBasics.Int64s[i], i, v) } } Uints: if cpyBasics.Uint != test.Uint { t.Errorf("Uint: got %v; want %v", cpyBasics.Uint, test.Uint) } if hasSameDataPointer(test.Uints, cpyBasics.Uints) { t.Error("Uints: address of copy was the same as original; they should be different") goto Uint8s } if len(cpyBasics.Uints) != len(test.Uints) { t.Errorf("Uints: len was %d; want %d", len(cpyBasics.Uints), len(test.Uints)) goto Uint8s } for i, v := range test.Uints { if v != cpyBasics.Uints[i] { t.Errorf("Uints: got %v at index %d of the copy; want %v", cpyBasics.Uints[i], i, v) } } Uint8s: if cpyBasics.Uint8 != test.Uint8 { t.Errorf("Uint8: got %v; want %v", cpyBasics.Uint8, test.Uint8) } if hasSameDataPointer(test.Uint8s, cpyBasics.Uint8s) { t.Error("Uint8s: address of copy was the same as original; they should be different") goto Uint16s } if len(cpyBasics.Uint8s) != len(test.Uint8s) { t.Errorf("Uint8s: len was %d; want %d", len(cpyBasics.Uint8s), len(test.Uint8s)) goto Uint16s } for i, v := range test.Uint8s { if v != cpyBasics.Uint8s[i] { t.Errorf("Uint8s: got %v at index %d of the copy; want %v", cpyBasics.Uint8s[i], i, v) } } Uint16s: if cpyBasics.Uint16 != test.Uint16 { t.Errorf("Uint16: got %v; want %v", cpyBasics.Uint16, test.Uint16) } if hasSameDataPointer(test.Uint16s, cpyBasics.Uint16s) { t.Error("Uint16s: address of copy was the same as original; they should be different") goto Uint32s } if len(cpyBasics.Uint16s) != len(test.Uint16s) { t.Errorf("Uint16s: len was %d; want %d", len(cpyBasics.Uint16s), len(test.Uint16s)) goto Uint32s } for i, v := range test.Uint16s { if v != cpyBasics.Uint16s[i] { t.Errorf("Uint16s: got %v at index %d of the copy; want %v", cpyBasics.Uint16s[i], i, v) } } Uint32s: if cpyBasics.Uint32 != test.Uint32 { t.Errorf("Uint32: got %v; want %v", cpyBasics.Uint32, test.Uint32) } if hasSameDataPointer(test.Uint32s, cpyBasics.Uint32s) { t.Error("Uint32s: address of copy was the same as original; they should be different") goto Uint64s } if len(cpyBasics.Uint32s) != len(test.Uint32s) { t.Errorf("Uint32s: len was %d; want %d", len(cpyBasics.Uint32s), len(test.Uint32s)) goto Uint64s } for i, v := range test.Uint32s { if v != cpyBasics.Uint32s[i] { t.Errorf("Uint32s: got %v at index %d of the copy; want %v", cpyBasics.Uint32s[i], i, v) } } Uint64s: if cpyBasics.Uint64 != test.Uint64 { t.Errorf("Uint64: got %v; want %v", cpyBasics.Uint64, test.Uint64) } if hasSameDataPointer(test.Uint64s, cpyBasics.Uint64s) { t.Error("Uint64s: address of copy was the same as original; they should be different") goto Float32s } if len(cpyBasics.Uint64s) != len(test.Uint64s) { t.Errorf("Uint64s: len was %d; want %d", len(cpyBasics.Uint64s), len(test.Uint64s)) goto Float32s } for i, v := range test.Uint64s { if v != cpyBasics.Uint64s[i] { t.Errorf("Uint64s: got %v at index %d of the copy; want %v", cpyBasics.Uint64s[i], i, v) } } Float32s: if cpyBasics.Float32 != test.Float32 { t.Errorf("Float32: got %v; want %v", cpyBasics.Float32, test.Float32) } if hasSameDataPointer(test.Float32s, cpyBasics.Float32s) { t.Error("Float32s: address of copy was the same as original; they should be different") goto Float64s } if len(cpyBasics.Float32s) != len(test.Float32s) { t.Errorf("Float32s: len was %d; want %d", len(cpyBasics.Float32s), len(test.Float32s)) goto Float64s } for i, v := range test.Float32s { if v != cpyBasics.Float32s[i] { t.Errorf("Float32s: got %v at index %d of the copy; want %v", cpyBasics.Float32s[i], i, v) } } Float64s: if cpyBasics.Float64 != test.Float64 { t.Errorf("Float64: got %v; want %v", cpyBasics.Float64, test.Float64) } if hasSameDataPointer(test.Float64s, cpyBasics.Float64s) { t.Error("Float64s: address of copy was the same as original; they should be different") goto Complex64s } if len(cpyBasics.Float64s) != len(test.Float64s) { t.Errorf("Float64s: len was %d; want %d", len(cpyBasics.Float64s), len(test.Float64s)) goto Complex64s } for i, v := range test.Float64s { if v != cpyBasics.Float64s[i] { t.Errorf("Float64s: got %v at index %d of the copy; want %v", cpyBasics.Float64s[i], i, v) } } Complex64s: if cpyBasics.Complex64 != test.Complex64 { t.Errorf("Complex64: got %v; want %v", cpyBasics.Complex64, test.Complex64) } if hasSameDataPointer(test.Complex64s, cpyBasics.Complex64s) { t.Error("Complex64s: address of copy was the same as original; they should be different") goto Complex128s } if len(cpyBasics.Complex64s) != len(test.Complex64s) { t.Errorf("Complex64s: len was %d; want %d", len(cpyBasics.Complex64s), len(test.Complex64s)) goto Complex128s } for i, v := range test.Complex64s { if v != cpyBasics.Complex64s[i] { t.Errorf("Complex64s: got %v at index %d of the copy; want %v", cpyBasics.Complex64s[i], i, v) } } Complex128s: if cpyBasics.Complex128 != test.Complex128 { t.Errorf("Complex128s: got %v; want %v", cpyBasics.Complex128s, test.Complex128s) } if hasSameDataPointer(test.Complex128s, cpyBasics.Complex128s) { t.Error("Complex128s: address of copy was the same as original; they should be different") goto Interfaces } if len(cpyBasics.Complex128s) != len(test.Complex128s) { t.Errorf("Complex128s: len was %d; want %d", len(cpyBasics.Complex128s), len(test.Complex128s)) goto Interfaces } for i, v := range test.Complex128s { if v != cpyBasics.Complex128s[i] { t.Errorf("Complex128s: got %v at index %d of the copy; want %v", cpyBasics.Complex128s[i], i, v) } } Interfaces: if cpyBasics.Interface != test.Interface { t.Errorf("Interface: got %v; want %v", cpyBasics.Interface, test.Interface) } if hasSameDataPointer(test.Interfaces, cpyBasics.Interfaces) { t.Error("Interfaces: address of copy was the same as original; they should be different") return } if len(cpyBasics.Interfaces) != len(test.Interfaces) { t.Errorf("Interfaces: len was %d; want %d", len(cpyBasics.Interfaces), len(test.Interfaces)) return } for i, v := range test.Interfaces { if v != cpyBasics.Interfaces[i] { t.Errorf("Interfaces: got %v at index %d of the copy; want %v", cpyBasics.Interfaces[i], i, v) } } } type A struct { Int int String string UintSl []uint NilSl []string Map map[string]int MapB map[string]*B SliceB []B B T time.Time } type B struct { Vals []string } type C struct { Vals []string } var AStruct = A{ Int: 42, String: "Konichiwa", UintSl: []uint{0, 1, 2, 3}, Map: map[string]int{"a": 1, "b": 2}, MapB: map[string]*B{ "hi": &B{Vals: []string{"hello", "bonjour"}}, "bye": &B{Vals: []string{"good-bye", "au revoir"}}, }, SliceB: []B{ B{Vals: []string{"Ciao", "Aloha"}}, }, B: B{Vals: []string{"42"}}, T: time.Now(), } func TestStructA(t *testing.T) { cpy, err := Copy(&AStruct) if err != nil { t.Fatal(err) } cpyA := cpy.(*A) if cpyA == &AStruct { t.Error("expected copy to have a different address than the original; it was the same") return } if cpyA.Int != AStruct.Int { t.Errorf("A.Int: got %v, want %v", cpyA.Int, AStruct.Int) } if cpyA.String != AStruct.String { t.Errorf("A.String: got %v; want %v", cpyA.String, AStruct.String) } if hasSameDataPointer(AStruct.UintSl, cpyA.UintSl) { t.Error("A.Uintsl: expected the copies address to be different; it wasn't") goto NilSl } if len(cpyA.UintSl) != len(AStruct.UintSl) { t.Errorf("A.UintSl: got len of %d, want %d", len(cpyA.UintSl), len(AStruct.UintSl)) goto NilSl } for i, v := range AStruct.UintSl { if cpyA.UintSl[i] != v { t.Errorf("A.UintSl %d: got %d, want %d", i, cpyA.UintSl[i], v) } } NilSl: if cpyA.NilSl != nil { t.Error("A.NilSl: expected slice to be nil, it wasn't") } if *(*uintptr)(unsafe.Pointer(&cpyA.Map)) == *(*uintptr)(unsafe.Pointer(&AStruct.Map)) { t.Error("A.Map: expected the copy's address to be different; it wasn't") goto AMapB } if len(cpyA.Map) != len(AStruct.Map) { t.Errorf("A.Map: got len of %d, want %d", len(cpyA.Map), len(AStruct.Map)) goto AMapB } for k, v := range AStruct.Map { val, ok := cpyA.Map[k] if !ok { t.Errorf("A.Map: expected the key %s to exist in the copy, it didn't", k) continue } if val != v { t.Errorf("A.Map[%s]: got %d, want %d", k, val, v) } } AMapB: if *(*uintptr)(unsafe.Pointer(&cpyA.MapB)) == *(*uintptr)(unsafe.Pointer(&AStruct.MapB)) { t.Error("A.MapB: expected the copy's address to be different; it wasn't") goto ASliceB } if len(cpyA.MapB) != len(AStruct.MapB) { t.Errorf("A.MapB: got len of %d, want %d", len(cpyA.MapB), len(AStruct.MapB)) goto ASliceB } for k, v := range AStruct.MapB { val, ok := cpyA.MapB[k] if !ok { t.Errorf("A.MapB: expected the key %s to exist in the copy, it didn't", k) continue } if unsafe.Pointer(val) == unsafe.Pointer(v) { t.Errorf("A.MapB[%s]: expected the addresses of the values to be different; they weren't", k) continue } // the slice headers should point to different data if hasSameDataPointer(v.Vals, val.Vals) { t.Errorf("%s: expected B's SliceHeaders to point to different Data locations; they did not.", k) continue } for i, vv := range v.Vals { if vv != val.Vals[i] { t.Errorf("A.MapB[%s].Vals[%d]: got %s want %s", k, i, vv, val.Vals[i]) } } } ASliceB: if hasSameDataPointer(AStruct.SliceB, cpyA.SliceB) { t.Error("A.SliceB: expected the copy's address to be different; it wasn't") goto B } if len(AStruct.SliceB) != len(cpyA.SliceB) { t.Errorf("A.SliceB: got length of %d; want %d", len(cpyA.SliceB), len(AStruct.SliceB)) goto B } for i := range AStruct.SliceB { if unsafe.Pointer(&AStruct.SliceB[i]) == unsafe.Pointer(&cpyA.SliceB[i]) { t.Errorf("A.SliceB[%d]: expected them to have different addresses, they didn't", i) continue } if hasSameDataPointer(AStruct.SliceB[i].Vals, cpyA.SliceB[i].Vals) { t.Errorf("A.SliceB[%d]: expected B.Vals SliceHeader.Data to point to different locations; they did not", i) continue } if len(AStruct.SliceB[i].Vals) != len(cpyA.SliceB[i].Vals) { t.Errorf("A.SliceB[%d]: expected B's vals to have the same length, they didn't", i) continue } for j, val := range AStruct.SliceB[i].Vals { if val != cpyA.SliceB[i].Vals[j] { t.Errorf("A.SliceB[%d].Vals[%d]: got %v; want %v", i, j, cpyA.SliceB[i].Vals[j], val) } } } B: if unsafe.Pointer(&AStruct.B) == unsafe.Pointer(&cpyA.B) { t.Error("A.B: expected them to have different addresses, they didn't") goto T } if hasSameDataPointer(AStruct.B.Vals, cpyA.B.Vals) { t.Error("A.B.Vals: expected the SliceHeaders.Data to point to different locations; they didn't") goto T } if len(AStruct.B.Vals) != len(cpyA.B.Vals) { t.Error("A.B.Vals: expected their lengths to be the same, they weren't") goto T } for i, v := range AStruct.B.Vals { if v != cpyA.B.Vals[i] { t.Errorf("A.B.Vals[%d]: got %s want %s", i, cpyA.B.Vals[i], v) } } T: if fmt.Sprintf("%p", &AStruct.T) == fmt.Sprintf("%p", &cpyA.T) { t.Error("A.T: expected them to have different addresses, they didn't") return } if AStruct.T != cpyA.T { t.Errorf("A.T: got %v, want %v", cpyA.T, AStruct.T) } } type Unexported struct { A string B int aa string bb int cc []int dd map[string]string } func TestUnexportedFields(t *testing.T) { u := &Unexported{ A: "A", B: 42, aa: "aa", bb: 42, cc: []int{1, 2, 3}, dd: map[string]string{"hello": "bonjour"}, } cpy, err := Copy(u) if err != nil { t.Fatal(err) } cpyUnexported := cpy.(*Unexported) if cpyUnexported == u { t.Error("expected addresses to be different, they weren't") return } if u.A != cpyUnexported.A { t.Errorf("Unexported.A: got %s want %s", cpyUnexported.A, u.A) } if u.B != cpyUnexported.B { t.Errorf("Unexported.A: got %d want %d", cpyUnexported.B, u.B) } if cpyUnexported.aa != "" { t.Errorf("Unexported.aa: unexported field should not be set, it was set to %s", cpyUnexported.aa) } if cpyUnexported.bb != 0 { t.Errorf("Unexported.bb: unexported field should not be set, it was set to %d", cpyUnexported.bb) } if cpyUnexported.cc != nil { t.Errorf("Unexported.cc: unexported field should not be set, it was set to %#v", cpyUnexported.cc) } if cpyUnexported.dd != nil { t.Errorf("Unexported.dd: unexported field should not be set, it was set to %#v", cpyUnexported.dd) } } // Note: this test will fail until https://github.com/golang/go/issues/15716 is // fixed and the version it is part of gets released. type T struct { time.Time } func TestTimeCopy(t *testing.T) { tests := []struct { Y int M time.Month D int h int m int s int nsec int TZ string }{ {2016, time.July, 4, 23, 11, 33, 3000, "America/New_York"}, {2015, time.October, 31, 9, 44, 23, 45935, "UTC"}, {2014, time.May, 5, 22, 01, 50, 219300, "Europe/Prague"}, } for i, test := range tests { l, err := time.LoadLocation(test.TZ) if err != nil { t.Errorf("%d: unexpected error: %s", i, err) continue } var x T x.Time = time.Date(test.Y, test.M, test.D, test.h, test.m, test.s, test.nsec, l) c, err := Copy(x) if err != nil { t.Fatal(err) } cpy := c.(T) if fmt.Sprintf("%p", &cpy) == fmt.Sprintf("%p", &x) { t.Errorf("%d: expected the copy to have a different address than the original value; they were the same: %p %p", i, &cpy, &x) continue } if x.UnixNano() != cpy.UnixNano() { t.Errorf("%d: nanotime: got %v; want %v", i, cpy.UnixNano(), x.UnixNano()) continue } if x.Location() != cpy.Location() { t.Errorf("%d: location: got %q; want %q", i, cpy.Location(), x.Location()) } } } func TestPointerToStruct(t *testing.T) { type Foo struct { Bar int } f := &Foo{Bar: 42} cpy, err := Copy(f) if err != nil { t.Fatal(err) } if f == cpy { t.Errorf("expected copy to point to a different location: orig: %p; copy: %p", f, cpy) } if !reflect.DeepEqual(f, cpy) { t.Errorf("expected the copy to be equal to the original (except for memory location); it wasn't: got %#v; want %#v", f, cpy) } } func TestIssue9(t *testing.T) { // simple pointer copy x := 42 testA := map[string]*int{ "a": nil, "b": &x, } copyA1, err := Copy(testA) if err != nil { t.Fatal(err) } copyA := copyA1.(map[string]*int) if unsafe.Pointer(&testA) == unsafe.Pointer(©A) { t.Fatalf("expected the map pointers to be different: testA: %v\tcopyA: %v", unsafe.Pointer(&testA), unsafe.Pointer(©A)) } if !reflect.DeepEqual(testA, copyA) { t.Errorf("got %#v; want %#v", copyA, testA) } if testA["b"] == copyA["b"] { t.Errorf("entries for 'b' pointed to the same address: %v; expected them to point to different addresses", testA["b"]) } // map copy type Foo struct { Alpha string } type Bar struct { Beta string Gamma int Delta *Foo } type Biz struct { Epsilon map[int]*Bar } testB := Biz{ Epsilon: map[int]*Bar{ 0: &Bar{}, 1: &Bar{ Beta: "don't panic", Gamma: 42, Delta: nil, }, 2: &Bar{ Beta: "sudo make me a sandwich.", Gamma: 11, Delta: &Foo{ Alpha: "okay.", }, }, }, } copyB1, err := Copy(testB) if err != nil { t.Fatal(err) } copyB := copyB1.(Biz) if !reflect.DeepEqual(testB, copyB) { t.Errorf("got %#v; want %#v", copyB, testB) return } // check that the maps point to different locations if unsafe.Pointer(&testB.Epsilon) == unsafe.Pointer(©B.Epsilon) { t.Fatalf("expected the map pointers to be different; they weren't: testB: %v\tcopyB: %v", unsafe.Pointer(&testB.Epsilon), unsafe.Pointer(©B.Epsilon)) } for k, v := range testB.Epsilon { if v == nil && copyB.Epsilon[k] == nil { continue } if v == nil && copyB.Epsilon[k] != nil { t.Errorf("%d: expected copy of a nil entry to be nil; it wasn't: %#v", k, copyB.Epsilon[k]) continue } if v == copyB.Epsilon[k] { t.Errorf("entries for '%d' pointed to the same address: %v; expected them to point to different addresses", k, v) continue } if v.Beta != copyB.Epsilon[k].Beta { t.Errorf("%d.Beta: got %q; want %q", k, copyB.Epsilon[k].Beta, v.Beta) } if v.Gamma != copyB.Epsilon[k].Gamma { t.Errorf("%d.Gamma: got %d; want %d", k, copyB.Epsilon[k].Gamma, v.Gamma) } if v.Delta == nil && copyB.Epsilon[k].Delta == nil { continue } if v.Delta == nil && copyB.Epsilon[k].Delta != nil { t.Errorf("%d.Delta: got %#v; want nil", k, copyB.Epsilon[k].Delta) } if v.Delta == copyB.Epsilon[k].Delta { t.Errorf("%d.Delta: expected the pointers to be different, they were the same: %v", k, v.Delta) continue } if v.Delta.Alpha != copyB.Epsilon[k].Delta.Alpha { t.Errorf("%d.Delta.Foo: got %q; want %q", k, v.Delta.Alpha, copyB.Epsilon[k].Delta.Alpha) } } // test that map keys are deep copied testC := map[*Foo][]string{ &Foo{Alpha: "Henry Dorsett Case"}: []string{ "Cutter", }, &Foo{Alpha: "Molly Millions"}: []string{ "Rose Kolodny", "Cat Mother", "Steppin' Razor", }, } copyC1, err := Copy(testC) if err != nil { t.Fatal(err) } copyC := copyC1.(map[*Foo][]string) if unsafe.Pointer(&testC) == unsafe.Pointer(©C) { t.Fatalf("expected the map pointers to be different; they weren't: testB: %v\tcopyB: %v", unsafe.Pointer(&testB.Epsilon), unsafe.Pointer(©B.Epsilon)) } // make sure the lengths are the same if len(testC) != len(copyC) { t.Fatalf("got len %d; want %d", len(copyC), len(testC)) } // check that everything was deep copied: since the key is a pointer, we check to // see if the pointers are different but the values being pointed to are the same. for k, v := range testC { for kk, vv := range copyC { if *kk == *k { if kk == k { t.Errorf("key pointers should be different: orig: %p; copy: %p", k, kk) } // check that the slices are the same but different if !reflect.DeepEqual(v, vv) { t.Errorf("expected slice contents to be the same; they weren't: orig: %v; copy: %v", v, vv) } if hasSameDataPointer(v, vv) { t.Errorf("expected the SliceHeaders.Data to point to different locations; they didn't: %v", (*reflect.SliceHeader)(unsafe.Pointer(&v)).Data) } break } } } type Bizz struct { *Foo } testD := map[Bizz]string{ Bizz{&Foo{"Neuromancer"}}: "Rio", Bizz{&Foo{"Wintermute"}}: "Berne", } copyD1, err := Copy(testD) if err != nil { t.Fatal(err) } copyD := copyD1.(map[Bizz]string) if len(copyD) != len(testD) { t.Fatalf("copy had %d elements; expected %d", len(copyD), len(testD)) } for k, v := range testD { var found bool for kk, vv := range copyD { if reflect.DeepEqual(k, kk) { found = true // check that Foo points to different locations if unsafe.Pointer(k.Foo) == unsafe.Pointer(kk.Foo) { t.Errorf("Expected Foo to point to different locations; they didn't: orig: %p; copy %p", k.Foo, kk.Foo) break } if *k.Foo != *kk.Foo { t.Errorf("Expected copy of the key's Foo field to have the same value as the original, it wasn't: orig: %#v; copy: %#v", k.Foo, kk.Foo) } if v != vv { t.Errorf("Expected the values to be the same; the weren't: got %v; want %v", vv, v) } } } if !found { t.Errorf("expected key %v to exist in the copy; it didn't", k) } } } type I struct { A string } func (i *I) DeepCopy() interface{} { return &I{A: "custom copy"} } type NestI struct { I *I } func TestInterface(t *testing.T) { i := &I{A: "A"} copied, err := Copy(i) if err != nil { t.Fatal(err) } copiedI := copied.(*I) if copiedI.A != "custom copy" { t.Errorf("expected value %v, but it's %v", "custom copy", copiedI.A) } // check for nesting values ni := &NestI{I: &I{A: "A"}} copiedNest, err := Copy(ni) if err != nil { t.Fatal(err) } copiedNestI := copiedNest.(*NestI) if copiedNestI.I.A != "custom copy" { t.Errorf("expected value %v, but it's %v", "custom copy", copiedNestI.I.A) } } func TestArray(t *testing.T) { type A [3][][]float64 var a = A{ { { 1.0, 2.0, 3.0, }, }, } distination, err := Copy(a) if err != nil { t.Fatal(err) } a[0][0][0] = 4.0 a[0][0][1] = 5.0 a[0][0][2] = 6.0 if distination.(A)[0][0][0] == a[0][0][0] { t.Fatal("Isn't deepcopied!", distination, a) } type I struct { I1 string } type B [3][]*I i := I{I1: "123"} var b = B{ { &i, }, } cb, err := Copy(b) if err != nil { t.Fatal(err) } i2 := I{I1: "456"} b[0][0] = &i2 if cb.(B)[0][0] == b[0][0] { t.Fatal("Isn't deepcopied!", distination, a) } } func TestDeepcopy2(t *testing.T) { { type a [1][]int var source = a{ { 1, }, } destination, err := Copy(source) if err != nil { t.Fatal(err) } if destination.(a)[0][0] != source[0][0] { t.Errorf("expected value %v, but it's %v", source[0][0], destination.(a)[0][0]) } source[0][0] = 0 if destination.(a)[0][0] == source[0][0] { t.Error("Fatal Isn't deepcopied!", destination, source) } } { type a [3][][]float64 var source = a{ { { 1.0, 2.0, 3.0, }, }, } distination, err := Copy(source) if err != nil { t.Fatal(err) } if distination.(a)[0][0][0] != source[0][0][0] { t.Errorf("expected value %v, but it's %v", source[0][0][0], distination.(a)[0][0][0]) } source[0][0][0] = 4.0 source[0][0][1] = 5.0 source[0][0][2] = 6.0 if distination.(a)[0][0][0] == source[0][0][0] { t.Error("Fatal Isn't deepcopied!", distination, source) } } } func TestArrayOfSomething(t *testing.T) { verifyCases := map[string]struct { modifyAndVerify func(t *testing.T) }{ // failed because the map(underlying is a pointer) will be copied directly "array of map": { modifyAndVerify: func(t *testing.T) { origin := [1]map[string]int{ { "1": 1, }, } copied, err := Copy(origin) if err != nil { t.Fatal(err) } assert.NotSame(t, &origin, &copied) assert.Equal(t, origin, copied) origin[0]["1"] = 999 assert.Equal(t, 1, copied.([1]map[string]int)[0]["1"]) }, }, // failed because the pointer of map will be copied directly "array of *map": { modifyAndVerify: func(t *testing.T) { origin := [1]*map[string]int{ { "1": 1, }, } copied, err := Copy(origin) if err != nil { t.Fatal(err) } assert.NotSame(t, origin, copied) assert.Equal(t, origin, copied) (*origin[0])["1"] = 999 assert.Equal(t, 1, (*copied.([1]*map[string]int)[0])["1"]) }, }, // failed because the pointer of int will be copied directly "array of *int": { modifyAndVerify: func(t *testing.T) { intp := func(i int) *int { return &i } arrayOfInt := [3]*int{intp(1), intp(2)} copied1, err := Copy(&arrayOfInt) if err != nil { t.Fatal(err) } copied := copied1.(*[3]*int) assert.NotSame(t, &arrayOfInt, copied) assert.NotSame(t, arrayOfInt[0], copied[0]) assert.Equal(t, arrayOfInt[0], copied[0]) arrayOfInt[0] = intp(999) assert.Equal(t, 1, *copied[0]) }, }, // succeed because int will be copied by value "array of int": { modifyAndVerify: func(t *testing.T) { arrayOfInt := [3]int{1, 2} copied, err := Copy(&arrayOfInt) if err != nil { t.Fatal(err) } assert.NotSame(t, &arrayOfInt, copied) assert.Equal(t, arrayOfInt[0], copied.(*[3]int)[0]) arrayOfInt[0] = 999 assert.Equal(t, 1, copied.(*[3]int)[0]) }, }, } for key, tt := range verifyCases { t.Run(key, tt.modifyAndVerify) } }