Skip to content

Commit

Permalink
helper/schema: Add Set.HashEqual
Browse files Browse the repository at this point in the history
Equality of schema.Sets gets tricky when dealing with nested sets -
Set.Equal only superficially compares the underlying maps and hence any
sets nested under the root sets cause issues.

This adds a simple method, HashEqual, that does a top-level hash
comparison, helping to work around this without any complex re-invention
of things like reflect.DeepEqual.

Of course, in order to make effective use of this function, the user
needs to make sure they are properly hashing their nested sets, however
this is trivial with things like HashResource.
  • Loading branch information
vancluever committed Aug 16, 2017
1 parent bb00fd4 commit ca42980
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 0 deletions.
25 changes: 25 additions & 0 deletions helper/schema/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,31 @@ func (s *Set) Equal(raw interface{}) bool {
return reflect.DeepEqual(s.m, other.m)
}

// HashEqual simply checks to the keys the top-level map to the keys in the
// other set's top-level map to see if they are equal. This obviously assumes
// you have a properly working hash function - use HashResource if in doubt.
func (s *Set) HashEqual(raw interface{}) bool {
other, ok := raw.(*Set)
if !ok {
return false
}

ks1 := make([]string, 0)
ks2 := make([]string, 0)

for k := range s.m {
ks1 = append(ks1, k)
}
for k := range other.m {
ks2 = append(ks2, k)
}

sort.Strings(ks1)
sort.Strings(ks2)

return reflect.DeepEqual(ks1, ks2)
}

func (s *Set) GoString() string {
return fmt.Sprintf("*Set(%#v)", s.m)
}
Expand Down
65 changes: 65 additions & 0 deletions helper/schema/set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,68 @@ func TestHashResource_nil(t *testing.T) {
t.Fatalf("Expected 0 when hashing nil, given: %d", idx)
}
}

func TestHashEqual(t *testing.T) {
nested := &Resource{
Schema: map[string]*Schema{
"foo": {
Type: TypeString,
Optional: true,
},
},
}
root := &Resource{
Schema: map[string]*Schema{
"bar": {
Type: TypeString,
Optional: true,
},
"nested": {
Type: TypeSet,
Optional: true,
Elem: nested,
},
},
}
n1 := map[string]interface{}{"foo": "bar"}
n2 := map[string]interface{}{"foo": "baz"}

r1 := map[string]interface{}{
"bar": "baz",
"nested": NewSet(HashResource(nested), []interface{}{n1}),
}
r2 := map[string]interface{}{
"bar": "qux",
"nested": NewSet(HashResource(nested), []interface{}{n2}),
}
s1 := NewSet(HashResource(root), []interface{}{r1})
s2 := NewSet(HashResource(root), []interface{}{r2})

cases := []struct {
name string
set *Set
compare *Set
expected bool
}{
{
name: "equal",
set: s1,
compare: s1,
expected: true,
},
{
name: "not equal",
set: s1,
compare: s2,
expected: false,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
actual := tc.set.HashEqual(tc.compare)
if tc.expected != actual {
t.Fatalf("expected %t, got %t", tc.expected, actual)
}
})
}
}

0 comments on commit ca42980

Please sign in to comment.