terraform: State.Equal needs to use reflect for rich types

Due to the change to `interface{}` we need to use `reflect.DeepEqual`
here. With the restriction of primitive types this should always be
safe. We'll never get functions, channels, etc.
This commit is contained in:
Mitchell Hashimoto 2017-02-23 13:59:11 -08:00
parent 9fc577547c
commit 41d2c145b2
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
2 changed files with 64 additions and 13 deletions

View File

@ -1633,13 +1633,11 @@ func (s *InstanceState) Equal(other *InstanceState) bool {
if len(s.Meta) != len(other.Meta) {
return false
}
for k, v := range s.Meta {
otherV, ok := other.Meta[k]
if !ok {
return false
}
if v != otherV {
if s.Meta != nil && other.Meta != nil {
// We only do the deep check if both are non-nil. If one is nil
// we treat it as equal since their lengths are both zero (check
// above).
if !reflect.DeepEqual(s.Meta, other.Meta) {
return false
}
}

View File

@ -325,17 +325,20 @@ func TestStateDeepCopy(t *testing.T) {
func TestStateEqual(t *testing.T) {
cases := []struct {
Name string
Result bool
One, Two *State
}{
// Nils
{
"one nil",
false,
nil,
&State{Version: 2},
},
{
"both nil",
true,
nil,
nil,
@ -343,6 +346,7 @@ func TestStateEqual(t *testing.T) {
// Different versions
{
"different state versions",
false,
&State{Version: 5},
&State{Version: 2},
@ -350,6 +354,7 @@ func TestStateEqual(t *testing.T) {
// Different modules
{
"different module states",
false,
&State{
Modules: []*ModuleState{
@ -362,6 +367,7 @@ func TestStateEqual(t *testing.T) {
},
{
"same module states",
true,
&State{
Modules: []*ModuleState{
@ -381,6 +387,7 @@ func TestStateEqual(t *testing.T) {
// Meta differs
{
"differing meta values with primitives",
false,
&State{
Modules: []*ModuleState{
@ -415,15 +422,61 @@ func TestStateEqual(t *testing.T) {
},
},
},
// Meta with complex types
{
"same meta with complex types",
true,
&State{
Modules: []*ModuleState{
&ModuleState{
Path: rootModulePath,
Resources: map[string]*ResourceState{
"test_instance.foo": &ResourceState{
Primary: &InstanceState{
Meta: map[string]interface{}{
"timeouts": map[string]interface{}{
"create": 42,
"read": "27",
},
},
},
},
},
},
},
},
&State{
Modules: []*ModuleState{
&ModuleState{
Path: rootModulePath,
Resources: map[string]*ResourceState{
"test_instance.foo": &ResourceState{
Primary: &InstanceState{
Meta: map[string]interface{}{
"timeouts": map[string]interface{}{
"create": 42,
"read": "27",
},
},
},
},
},
},
},
},
},
}
for i, tc := range cases {
if tc.One.Equal(tc.Two) != tc.Result {
t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String())
}
if tc.Two.Equal(tc.One) != tc.Result {
t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String())
}
t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
if tc.One.Equal(tc.Two) != tc.Result {
t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String())
}
if tc.Two.Equal(tc.One) != tc.Result {
t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String())
}
})
}
}