helper/schema: Add Set.HashEqual

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.
This commit is contained in:
Chris Marchesi 2017-08-15 21:45:39 -07:00
parent bb00fd47c0
commit ca42980e49
No known key found for this signature in database
GPG Key ID: 335B84AFEC615CE4
2 changed files with 90 additions and 0 deletions

View File

@ -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)
}

View File

@ -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)
}
})
}
}