in cluster-autoscaler/cloudprovider/magnum/gophercloud/testhelper/convenience.go [53:195]
func deepDiffEqual(expected, actual reflect.Value, visited map[visit]bool, path []string, logDifference diffLogger) {
defer func() {
// Fall back to the regular reflect.DeepEquals function.
if r := recover(); r != nil {
var e, a interface{}
if expected.IsValid() {
e = expected.Interface()
}
if actual.IsValid() {
a = actual.Interface()
}
if !reflect.DeepEqual(e, a) {
logDifference(path, e, a)
}
}
}()
if !expected.IsValid() && actual.IsValid() {
logDifference(path, nil, actual.Interface())
return
}
if expected.IsValid() && !actual.IsValid() {
logDifference(path, expected.Interface(), nil)
return
}
if !expected.IsValid() && !actual.IsValid() {
return
}
hard := func(k reflect.Kind) bool {
switch k {
case reflect.Array, reflect.Map, reflect.Slice, reflect.Struct:
return true
}
return false
}
if expected.CanAddr() && actual.CanAddr() && hard(expected.Kind()) {
addr1 := expected.UnsafeAddr()
addr2 := actual.UnsafeAddr()
if addr1 > addr2 {
addr1, addr2 = addr2, addr1
}
if addr1 == addr2 {
// References are identical. We can short-circuit
return
}
typ := expected.Type()
v := visit{addr1, addr2, typ}
if visited[v] {
// Already visited.
return
}
// Remember this visit for later.
visited[v] = true
}
switch expected.Kind() {
case reflect.Array:
for i := 0; i < expected.Len(); i++ {
hop := append(path, fmt.Sprintf("[%d]", i))
deepDiffEqual(expected.Index(i), actual.Index(i), visited, hop, logDifference)
}
return
case reflect.Slice:
if expected.IsNil() != actual.IsNil() {
logDifference(path, expected.Interface(), actual.Interface())
return
}
if expected.Len() == actual.Len() && expected.Pointer() == actual.Pointer() {
return
}
for i := 0; i < expected.Len(); i++ {
hop := append(path, fmt.Sprintf("[%d]", i))
deepDiffEqual(expected.Index(i), actual.Index(i), visited, hop, logDifference)
}
return
case reflect.Interface:
if expected.IsNil() != actual.IsNil() {
logDifference(path, expected.Interface(), actual.Interface())
return
}
deepDiffEqual(expected.Elem(), actual.Elem(), visited, path, logDifference)
return
case reflect.Ptr:
deepDiffEqual(expected.Elem(), actual.Elem(), visited, path, logDifference)
return
case reflect.Struct:
for i, n := 0, expected.NumField(); i < n; i++ {
field := expected.Type().Field(i)
hop := append(path, "."+field.Name)
deepDiffEqual(expected.Field(i), actual.Field(i), visited, hop, logDifference)
}
return
case reflect.Map:
if expected.IsNil() != actual.IsNil() {
logDifference(path, expected.Interface(), actual.Interface())
return
}
if expected.Len() == actual.Len() && expected.Pointer() == actual.Pointer() {
return
}
var keys []reflect.Value
if expected.Len() >= actual.Len() {
keys = expected.MapKeys()
} else {
keys = actual.MapKeys()
}
for _, k := range keys {
expectedValue := expected.MapIndex(k)
actualValue := actual.MapIndex(k)
if !expectedValue.IsValid() {
logDifference(path, nil, actual.Interface())
return
}
if !actualValue.IsValid() {
logDifference(path, expected.Interface(), nil)
return
}
hop := append(path, fmt.Sprintf("[%v]", k))
deepDiffEqual(expectedValue, actualValue, visited, hop, logDifference)
}
return
case reflect.Func:
if expected.IsNil() != actual.IsNil() {
logDifference(path, expected.Interface(), actual.Interface())
}
return
default:
if expected.Interface() != actual.Interface() {
logDifference(path, expected.Interface(), actual.Interface())
}
}
}