mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 13:37:30 +00:00
Make unexported fields panic (informatively)
...Also fix some incorrect calls to semantic.DeepEqual, and a bug where it returned true incorrectly.
This commit is contained in:
parent
2902028476
commit
3ef3777192
@ -60,6 +60,9 @@ var Semantic = conversion.EqualitiesOrDie(
|
|||||||
}
|
}
|
||||||
return a.Amount.Cmp(b.Amount) == 0
|
return a.Amount.Cmp(b.Amount) == 0
|
||||||
},
|
},
|
||||||
|
func(a, b util.Time) bool {
|
||||||
|
return a.Unix() == b.Unix()
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
var standardResources = util.NewStringSet(
|
var standardResources = util.NewStringSet(
|
||||||
|
@ -27,7 +27,7 @@ import (
|
|||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
// "reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -64,7 +64,7 @@ func TestRequestWithErrorWontChange(t *testing.T) {
|
|||||||
if changed != &r {
|
if changed != &r {
|
||||||
t.Errorf("returned request should point to the same object")
|
t.Errorf("returned request should point to the same object")
|
||||||
}
|
}
|
||||||
if !api.Semantic.DeepDerivative(changed, &original) {
|
if !reflect.DeepEqual(changed, &original) {
|
||||||
t.Errorf("expected %#v, got %#v", &original, changed)
|
t.Errorf("expected %#v, got %#v", &original, changed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ package conversion
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Equalities is a map from type to a function comparing two values of
|
// Equalities is a map from type to a function comparing two values of
|
||||||
@ -99,10 +100,36 @@ type visit struct {
|
|||||||
typ reflect.Type
|
typ reflect.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unexportedTypePanic is thrown when you use this DeepEqual on something that has an
|
||||||
|
// unexported type. It indicates a programmer error, so should not occur at runtime,
|
||||||
|
// which is why it's not public and thus impossible to catch.
|
||||||
|
type unexportedTypePanic []reflect.Type
|
||||||
|
|
||||||
|
func (u unexportedTypePanic) Error() string { return u.String() }
|
||||||
|
func (u unexportedTypePanic) String() string {
|
||||||
|
strs := make([]string, len(u))
|
||||||
|
for i, t := range u {
|
||||||
|
strs[i] = fmt.Sprintf("%v", t)
|
||||||
|
}
|
||||||
|
return "an unexported field was encountered, nested like this: " + strings.Join(strs, " -> ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeUsefulPanic(v reflect.Value) {
|
||||||
|
if x := recover(); x != nil {
|
||||||
|
if u, ok := x.(unexportedTypePanic); ok {
|
||||||
|
u = append(unexportedTypePanic{v.Type()}, u...)
|
||||||
|
x = u
|
||||||
|
}
|
||||||
|
panic(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Tests for deep equality using reflected types. The map argument tracks
|
// Tests for deep equality using reflected types. The map argument tracks
|
||||||
// comparisons that have already been seen, which allows short circuiting on
|
// comparisons that have already been seen, which allows short circuiting on
|
||||||
// recursive types.
|
// recursive types.
|
||||||
func (e Equalities) deepValueEqual(v1, v2 reflect.Value, visited map[visit]bool, depth int) bool {
|
func (e Equalities) deepValueEqual(v1, v2 reflect.Value, visited map[visit]bool, depth int) bool {
|
||||||
|
defer makeUsefulPanic(v1)
|
||||||
|
|
||||||
if !v1.IsValid() || !v2.IsValid() {
|
if !v1.IsValid() || !v2.IsValid() {
|
||||||
return v1.IsValid() == v2.IsValid()
|
return v1.IsValid() == v2.IsValid()
|
||||||
}
|
}
|
||||||
@ -207,10 +234,10 @@ func (e Equalities) deepValueEqual(v1, v2 reflect.Value, visited map[visit]bool,
|
|||||||
return false
|
return false
|
||||||
default:
|
default:
|
||||||
// Normal equality suffices
|
// Normal equality suffices
|
||||||
if v1.CanInterface() && v2.CanInterface() {
|
if !v1.CanInterface() || !v2.CanInterface() {
|
||||||
return v1.Interface() == v2.Interface()
|
panic(unexportedTypePanic{})
|
||||||
}
|
}
|
||||||
return v1.CanInterface() == v2.CanInterface()
|
return v1.Interface() == v2.Interface()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,7 +248,8 @@ func (e Equalities) deepValueEqual(v1, v2 reflect.Value, visited map[visit]bool,
|
|||||||
//
|
//
|
||||||
// An empty slice *is* equal to a nil slice for our purposes; same for maps.
|
// An empty slice *is* equal to a nil slice for our purposes; same for maps.
|
||||||
//
|
//
|
||||||
// Unexported field members are not compared.
|
// Unexported field members cannot be compared and will cause an imformative panic; you must add an Equality
|
||||||
|
// function for these types.
|
||||||
func (e Equalities) DeepEqual(a1, a2 interface{}) bool {
|
func (e Equalities) DeepEqual(a1, a2 interface{}) bool {
|
||||||
if a1 == nil || a2 == nil {
|
if a1 == nil || a2 == nil {
|
||||||
return a1 == a2
|
return a1 == a2
|
||||||
@ -235,6 +263,8 @@ func (e Equalities) DeepEqual(a1, a2 interface{}) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e Equalities) deepValueDerive(v1, v2 reflect.Value, visited map[visit]bool, depth int) bool {
|
func (e Equalities) deepValueDerive(v1, v2 reflect.Value, visited map[visit]bool, depth int) bool {
|
||||||
|
defer makeUsefulPanic(v1)
|
||||||
|
|
||||||
if !v1.IsValid() || !v2.IsValid() {
|
if !v1.IsValid() || !v2.IsValid() {
|
||||||
return v1.IsValid() == v2.IsValid()
|
return v1.IsValid() == v2.IsValid()
|
||||||
}
|
}
|
||||||
@ -347,10 +377,10 @@ func (e Equalities) deepValueDerive(v1, v2 reflect.Value, visited map[visit]bool
|
|||||||
return false
|
return false
|
||||||
default:
|
default:
|
||||||
// Normal equality suffices
|
// Normal equality suffices
|
||||||
if v1.CanInterface() && v2.CanInterface() {
|
if !v1.CanInterface() || !v2.CanInterface() {
|
||||||
return v1.Interface() == v2.Interface()
|
panic(unexportedTypePanic{})
|
||||||
}
|
}
|
||||||
return v1.CanInterface() == v2.CanInterface()
|
return v1.Interface() == v2.Interface()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -378,7 +379,7 @@ func TestFillCurrentState(t *testing.T) {
|
|||||||
if controller.Status.Replicas != 2 {
|
if controller.Status.Replicas != 2 {
|
||||||
t.Errorf("expected 2, got: %d", controller.Status.Replicas)
|
t.Errorf("expected 2, got: %d", controller.Status.Replicas)
|
||||||
}
|
}
|
||||||
if !api.Semantic.DeepEqual(fakeLister.s, labels.Set(controller.Spec.Selector).AsSelector()) {
|
if !reflect.DeepEqual(fakeLister.s, labels.Set(controller.Spec.Selector).AsSelector()) {
|
||||||
t.Errorf("unexpected output: %#v %#v", labels.Set(controller.Spec.Selector).AsSelector(), fakeLister.s)
|
t.Errorf("unexpected output: %#v %#v", labels.Set(controller.Spec.Selector).AsSelector(), fakeLister.s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user