From 3a963b0da5be907985c7443341618967b60607c4 Mon Sep 17 00:00:00 2001 From: "Dr. Stefan Schimanski" Date: Wed, 15 Feb 2017 10:16:05 +0100 Subject: [PATCH] pkg/api/testing: add deepcopy smoke test to roundtrip test --- .../apimachinery/pkg/api/testing/roundtrip.go | 9 ++ .../apimachinery/pkg/api/testing/valuefuzz.go | 86 +++++++++++++++++++ .../pkg/api/testing/valuefuzz_test.go | 73 ++++++++++++++++ vendor/BUILD | 8 ++ 4 files changed, 176 insertions(+) create mode 100644 staging/src/k8s.io/apimachinery/pkg/api/testing/valuefuzz.go create mode 100644 staging/src/k8s.io/apimachinery/pkg/api/testing/valuefuzz_test.go diff --git a/staging/src/k8s.io/apimachinery/pkg/api/testing/roundtrip.go b/staging/src/k8s.io/apimachinery/pkg/api/testing/roundtrip.go index 3ab60ca851f..217614ce7b9 100644 --- a/staging/src/k8s.io/apimachinery/pkg/api/testing/roundtrip.go +++ b/staging/src/k8s.io/apimachinery/pkg/api/testing/roundtrip.go @@ -272,6 +272,15 @@ func roundTrip(t *testing.T, scheme *runtime.Scheme, codec runtime.Codec, object t.Errorf("3: %v: diff: %v\nCodec: %#v", name, diff.ObjectReflectDiff(object, obj3), codec) return } + + // do structure-preserving fuzzing of the deep-copied object. If it shares anything with the original, + // the deep-copy was actually only a shallow copy. Then original and obj3 will be different after fuzzing. + // NOTE: we use the encoding+decoding here as an alternative, guaranteed deep-copy to compare against. + ValueFuzz(object) + if !apiequality.Semantic.DeepEqual(original, obj3) { + t.Errorf("0: %v: fuzzing a copy altered the original, diff: %v", name, diff.ObjectReflectDiff(original, object)) + return + } } // dataAsString returns the given byte array as a string; handles detecting diff --git a/staging/src/k8s.io/apimachinery/pkg/api/testing/valuefuzz.go b/staging/src/k8s.io/apimachinery/pkg/api/testing/valuefuzz.go new file mode 100644 index 00000000000..4cb03b7b0de --- /dev/null +++ b/staging/src/k8s.io/apimachinery/pkg/api/testing/valuefuzz.go @@ -0,0 +1,86 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package testing + +import ( + "reflect" +) + +// ValueFuzz recursively changes all basic type values in an object. Any kind of references will not +// be touch, i.e. the addresses of slices, maps, pointers will stay unchanged. +func ValueFuzz(obj interface{}) { + valueFuzz(reflect.ValueOf(obj)) +} + +func valueFuzz(obj reflect.Value) { + switch obj.Kind() { + case reflect.Array: + for i := 0; i < obj.Len(); i++ { + valueFuzz(obj.Index(i)) + } + case reflect.Slice: + if obj.IsNil() { + // TODO: set non-nil value + } else { + for i := 0; i < obj.Len(); i++ { + valueFuzz(obj.Index(i)) + } + } + case reflect.Interface, reflect.Ptr: + if obj.IsNil() { + // TODO: set non-nil value + } else { + valueFuzz(obj.Elem()) + } + case reflect.Struct: + for i, n := 0, obj.NumField(); i < n; i++ { + valueFuzz(obj.Field(i)) + } + case reflect.Map: + if obj.IsNil() { + // TODO: set non-nil value + } else { + for _, k := range obj.MapKeys() { + // map values are not addressable. We need a copy. + v := obj.MapIndex(k) + copy := reflect.New(v.Type()) + copy.Elem().Set(v) + valueFuzz(copy.Elem()) + obj.SetMapIndex(k, copy.Elem()) + } + // TODO: set some new value + } + case reflect.Func: // ignore, we don't have function types in our API + default: + if !obj.CanSet() { + return + } + switch obj.Kind() { + case reflect.String: + obj.SetString(obj.String() + "x") + case reflect.Bool: + obj.SetBool(!obj.Bool()) + case reflect.Float32, reflect.Float64: + obj.SetFloat(obj.Float()*2.0 + 1.0) + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + obj.SetInt(obj.Int() + 1) + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + obj.SetUint(obj.Uint() + 1) + default: + } + } +} diff --git a/staging/src/k8s.io/apimachinery/pkg/api/testing/valuefuzz_test.go b/staging/src/k8s.io/apimachinery/pkg/api/testing/valuefuzz_test.go new file mode 100644 index 00000000000..50191e8c565 --- /dev/null +++ b/staging/src/k8s.io/apimachinery/pkg/api/testing/valuefuzz_test.go @@ -0,0 +1,73 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package testing + +import "testing" + +func TestValueFuzz(t *testing.T) { + type ( + Y struct { + I int + B bool + F float32 + U uint + } + X struct { + Ptr *X + Y Y + Map map[string]int + Slice []int + } + ) + + x := X{ + Ptr: &X{}, + Map: map[string]int{"foo": 42}, + Slice: []int{1,2,3}, + } + + p := x.Ptr + m := x.Map + s := x.Slice + + ValueFuzz(x) + + if x.Ptr.Y.I == 0 { + t.Errorf("x.Ptr.Y.I should have changed") + } + + if x.Map["foo"] == 42 { + t.Errorf("x.Map[foo] should have changed") + } + + if x.Slice[0] == 1 { + t.Errorf("x.Slice[0] should have changed") + } + + if x.Ptr != p { + t.Errorf("x.Ptr changed") + } + + m["foo"] = 7 + if x.Map["foo"] != m["foo"] { + t.Errorf("x.Map changed") + } + s[0] = 7 + if x.Slice[0] != s[0] { + t.Errorf("x.Slice changed") + } +} diff --git a/vendor/BUILD b/vendor/BUILD index d2b1e11c1b0..2209d074523 100644 --- a/vendor/BUILD +++ b/vendor/BUILD @@ -13851,6 +13851,7 @@ go_library( "k8s.io/apimachinery/pkg/api/testing/codec.go", "k8s.io/apimachinery/pkg/api/testing/fuzzer.go", "k8s.io/apimachinery/pkg/api/testing/roundtrip.go", + "k8s.io/apimachinery/pkg/api/testing/valuefuzz.go", ], tags = ["automanaged"], deps = [ @@ -16897,3 +16898,10 @@ go_test( tags = ["automanaged"], deps = ["//vendor:k8s.io/apimachinery/pkg/api/testing"], ) + +go_test( + name = "k8s.io/apimachinery/pkg/api/testing_test", + srcs = ["k8s.io/apimachinery/pkg/api/testing/valuefuzz_test.go"], + library = ":k8s.io/apimachinery/pkg/api/testing", + tags = ["automanaged"], +)