Skip to content
Permalink
Browse files

tpl/collections: Properly handle pointer types in complement/symdiff

We cannot compare them by values, because that gets `hash of unhashable type` for the prime use case.
  • Loading branch information...
bep committed Nov 7, 2018
1 parent d212f60 commit 79a06aa4b64b526c242dfa41f2c7bc24e1352d5b
Showing with 20 additions and 11 deletions.
  1. +15 −4 tpl/collections/complement_test.go
  2. +1 −3 tpl/collections/reflect_helpers.go
  3. +4 −4 tpl/collections/symdiff_test.go
@@ -23,6 +23,13 @@ import (
"github.com/stretchr/testify/require"
)

type StructWithSlice struct {
A string
B []string
}

type StructWithSlicePointers []*StructWithSlice

func TestComplement(t *testing.T) {
t.Parallel()

@@ -33,10 +40,13 @@ func TestComplement(t *testing.T) {
s1 := []TstX{TstX{A: "a"}, TstX{A: "b"}, TstX{A: "d"}, TstX{A: "e"}}
s2 := []TstX{TstX{A: "b"}, TstX{A: "e"}}

xa, xd := &TstX{A: "a"}, &TstX{A: "d"}
xa, xb, xd, xe := &StructWithSlice{A: "a"}, &StructWithSlice{A: "b"}, &StructWithSlice{A: "d"}, &StructWithSlice{A: "e"}

sp1 := []*StructWithSlice{xa, xb, xd, xe}
sp2 := []*StructWithSlice{xb, xe}

sp1 := []*TstX{xa, &TstX{A: "b"}, xd, &TstX{A: "e"}}
sp2 := []*TstX{&TstX{A: "b"}, &TstX{A: "e"}}
sp1_2 := StructWithSlicePointers{xa, xb, xd, xe}
sp2_2 := StructWithSlicePointers{xb, xe}

for i, test := range []struct {
s interface{}
@@ -49,7 +59,8 @@ func TestComplement(t *testing.T) {
{[]int{1, 2, 3, 4, 5}, []interface{}{[]int{1, 3}, []string{"a", "b"}, []int{1, 2}}, []int{4, 5}},
{[]int{1, 2, 3, 4, 5}, []interface{}{[]int64{1, 3}}, []int{2, 4, 5}},
{s1, []interface{}{s2}, []TstX{TstX{A: "a"}, TstX{A: "d"}}},
{sp1, []interface{}{sp2}, []*TstX{xa, xd}},
{sp1, []interface{}{sp2}, []*StructWithSlice{xa, xd}},
{sp1_2, []interface{}{sp2_2}, StructWithSlicePointers{xa, xd}},

// Errors
{[]string{"a", "b", "c"}, []interface{}{"error"}, false},
@@ -43,7 +43,6 @@ func numberToFloat(v reflect.Value) (float64, error) {
}

// normalizes different numeric types to make them comparable.
// If not, any pointer will be unwrapped.
func normalize(v reflect.Value) interface{} {
k := v.Kind()

@@ -53,8 +52,6 @@ func normalize(v reflect.Value) interface{} {
if err == nil {
return f
}
case k == reflect.Ptr:
v = v.Elem()
}

return v.Interface()
@@ -70,6 +67,7 @@ func collectIdentities(seqs ...interface{}) (map[interface{}]bool, error) {
case reflect.Array, reflect.Slice:
for i := 0; i < v.Len(); i++ {
ev, _ := indirectInterface(v.Index(i))

if !ev.Type().Comparable() {
return nil, errors.New("elements must be comparable")
}
@@ -33,10 +33,10 @@ func TestSymDiff(t *testing.T) {
s1 := []TstX{TstX{A: "a"}, TstX{A: "b"}}
s2 := []TstX{TstX{A: "a"}, TstX{A: "e"}}

xa, xd := &TstX{A: "a"}, &TstX{A: "d"}
xa, xb, xd, xe := &StructWithSlice{A: "a"}, &StructWithSlice{A: "b"}, &StructWithSlice{A: "d"}, &StructWithSlice{A: "e"}

sp1 := []*TstX{xa, &TstX{A: "b"}, xd, &TstX{A: "e"}}
sp2 := []*TstX{&TstX{A: "b"}, &TstX{A: "e"}}
sp1 := []*StructWithSlice{xa, xb, xd, xe}
sp2 := []*StructWithSlice{xb, xe}

for i, test := range []struct {
s1 interface{}
@@ -49,7 +49,7 @@ func TestSymDiff(t *testing.T) {
{[]int{1, 2, 3}, []int{3, 4}, []int{1, 2, 4}},
{[]int{1, 2, 3}, []int64{3, 4}, []int{1, 2, 4}},
{s1, s2, []TstX{TstX{A: "b"}, TstX{A: "e"}}},
{sp1, sp2, []*TstX{xa, xd}},
{sp1, sp2, []*StructWithSlice{xa, xd}},

// Errors
{"error", "error", false},

0 comments on commit 79a06aa

Please sign in to comment.
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.