func deepDiffEqual()

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