diff --git a/pkg/api/helper_test.go b/pkg/api/helper_test.go index d39e544ad37..37eab722af3 100644 --- a/pkg/api/helper_test.go +++ b/pkg/api/helper_test.go @@ -25,54 +25,55 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/fsouza/go-dockerclient" + "github.com/google/gofuzz" ) -var fuzzIters = flag.Int("fuzz_iters", 3, "How many fuzzing iterations to do.") +var fuzzIters = flag.Int("fuzz_iters", 50, "How many fuzzing iterations to do.") // apiObjectFuzzer can randomly populate api objects. -var apiObjectFuzzer = util.NewFuzzer( - func(j *JSONBase) { +var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs( + func(j *JSONBase, c fuzz.Continue) { // We have to customize the randomization of JSONBases because their // APIVersion and Kind must remain blank in memory. j.APIVersion = "" j.Kind = "" - j.ID = util.RandString() + j.ID = c.RandString() // TODO: Fix JSON/YAML packages and/or write custom encoding // for uint64's. Somehow the LS *byte* of this is lost, but // only when all 8 bytes are set. - j.ResourceVersion = util.RandUint64() >> 8 - j.SelfLink = util.RandString() - j.CreationTimestamp = util.RandString() + j.ResourceVersion = c.RandUint64() >> 8 + j.SelfLink = c.RandString() + j.CreationTimestamp = c.RandString() }, - func(intstr *util.IntOrString) { + func(intstr *util.IntOrString, c fuzz.Continue) { // util.IntOrString will panic if its kind is set wrong. - if util.RandBool() { + if c.RandBool() { intstr.Kind = util.IntstrInt - intstr.IntVal = int(util.RandUint64()) + intstr.IntVal = int(c.RandUint64()) intstr.StrVal = "" } else { intstr.Kind = util.IntstrString intstr.IntVal = 0 - intstr.StrVal = util.RandString() + intstr.StrVal = c.RandString() } }, - func(u64 *uint64) { + func(u64 *uint64, c fuzz.Continue) { // TODO: uint64's are NOT handled right. - *u64 = util.RandUint64() >> 8 + *u64 = c.RandUint64() >> 8 }, - func(pb map[docker.Port][]docker.PortBinding) { + func(pb map[docker.Port][]docker.PortBinding, c fuzz.Continue) { // This is necessary because keys with nil values get omitted. // TODO: Is this a bug? - pb[docker.Port(util.RandString())] = []docker.PortBinding{ - {util.RandString(), util.RandString()}, - {util.RandString(), util.RandString()}, + pb[docker.Port(c.RandString())] = []docker.PortBinding{ + {c.RandString(), c.RandString()}, + {c.RandString(), c.RandString()}, } }, - func(pm map[string]docker.PortMapping) { + func(pm map[string]docker.PortMapping, c fuzz.Continue) { // This is necessary because keys with nil values get omitted. // TODO: Is this a bug? - pm[util.RandString()] = docker.PortMapping{ - util.RandString(): util.RandString(), + pm[c.RandString()] = docker.PortMapping{ + c.RandString(): c.RandString(), } }, ) diff --git a/pkg/conversion/converter_test.go b/pkg/conversion/converter_test.go index 60fbd8bdbc6..8e33e7e94e9 100644 --- a/pkg/conversion/converter_test.go +++ b/pkg/conversion/converter_test.go @@ -21,7 +21,7 @@ import ( "reflect" "testing" - "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + "github.com/google/gofuzz" ) func TestConverter_CallsRegisteredFunctions(t *testing.T) { @@ -95,7 +95,7 @@ func TestConverter_fuzz(t *testing.T) { {newAnonType(), &TestType1{}, newAnonType()}, } - f := util.NewFuzzer() + f := fuzz.New().NilChance(.5).NumElements(0, 100) c := NewConverter() for i, item := range table { @@ -189,7 +189,7 @@ func TestConverter_flags(t *testing.T) { shouldSucceed: true, }, } - f := util.NewFuzzer() + f := fuzz.New().NilChance(.5).NumElements(0, 100) c := NewConverter() for i, item := range table { diff --git a/pkg/conversion/scheme_test.go b/pkg/conversion/scheme_test.go index 642585cc21f..0f4d7f2a4e1 100644 --- a/pkg/conversion/scheme_test.go +++ b/pkg/conversion/scheme_test.go @@ -24,9 +24,10 @@ import ( "testing" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + "github.com/google/gofuzz" ) -var fuzzIters = flag.Int("fuzz_iters", 10, "How many fuzzing iterations to do.") +var fuzzIters = flag.Int("fuzz_iters", 50, "How many fuzzing iterations to do.") // Test a weird version/kind embedding format. type MyWeirdCustomEmbeddedVersionKindField struct { @@ -98,25 +99,25 @@ type ExternalInternalSame struct { } // TestObjectFuzzer can randomly populate all the above objects. -var TestObjectFuzzer = util.NewFuzzer( - func(j *MyWeirdCustomEmbeddedVersionKindField) { +var TestObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 100).Funcs( + func(j *MyWeirdCustomEmbeddedVersionKindField, c fuzz.Continue) { // We have to customize the randomization of MyWeirdCustomEmbeddedVersionKindFields because their // APIVersion and Kind must remain blank in memory. j.APIVersion = "" j.ObjectKind = "" - j.ID = util.RandString() + j.ID = c.RandString() }, - func(u *uint64) { + func(u *uint64, c fuzz.Continue) { // TODO: Fix JSON/YAML packages and/or write custom encoding // for uint64's. Somehow the LS *byte* of this is lost, but // only when all 8 bytes are set. - *u = util.RandUint64() >> 8 + *u = c.RandUint64() >> 8 }, - func(u *uint) { + func(u *uint, c fuzz.Continue) { // TODO: Fix JSON/YAML packages and/or write custom encoding // for uint64's. Somehow the LS *byte* of this is lost, but // only when all 8 bytes are set. - *u = uint(util.RandUint64() >> 8) + *u = uint(c.RandUint64() >> 8) }, ) diff --git a/pkg/util/fuzz.go b/pkg/util/fuzz.go deleted file mode 100644 index f76bcd3b89e..00000000000 --- a/pkg/util/fuzz.go +++ /dev/null @@ -1,270 +0,0 @@ -/* -Copyright 2014 Google Inc. All rights reserved. - -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 util - -import ( - "fmt" - "math/rand" - "reflect" -) - -// Fuzzer knows how to fill any object with random fields. -type Fuzzer struct { - customFuzz map[reflect.Type]func(reflect.Value) -} - -// NewFuzzer returns a new Fuzzer with the given custom fuzzing functions. -// -// Each entry in fuzzFuncs must be a function with one parameter, which will -// be the variable we wish that function to fill with random data. For this -// to be effective, the variable type should be either a pointer or a map. -// -// These functions are called sensibly, e.g., if you wanted custom string -// fuzzing, the function `func(s *string)` would get called and passed the -// address of strings. Maps and pointers will be made/new'd for you. For -// slices, it doesn't make much sense to pre-create them--Fuzzer doesn't -// know how long you want your slice--so take a pointer to a slice, and -// make it yourself. (If you don't want your map/pointer type pre-made, -// take a pointer to it, and make it yourself.) -// -// TODO: Take a source of randomness for deterministic, repeatable fuzzing. -// TODO: Make probability of getting a nil customizable. -func NewFuzzer(fuzzFuncs ...interface{}) *Fuzzer { - f := &Fuzzer{ - map[reflect.Type]func(reflect.Value){}, - } - for i := range fuzzFuncs { - v := reflect.ValueOf(fuzzFuncs[i]) - if v.Kind() != reflect.Func { - panic("Need only funcs!") - } - t := v.Type() - if t.NumIn() != 1 || t.NumOut() != 0 { - panic("Need 1 in and 0 out params!") - } - argT := t.In(0) - switch argT.Kind() { - case reflect.Ptr, reflect.Map: - default: - panic("fuzzFunc must take pointer or map type") - } - f.customFuzz[argT] = func(toFuzz reflect.Value) { - if toFuzz.Type().AssignableTo(argT) { - v.Call([]reflect.Value{toFuzz}) - } else if toFuzz.Type().ConvertibleTo(argT) { - v.Call([]reflect.Value{toFuzz.Convert(argT)}) - } else { - panic(fmt.Errorf("%#v neither ConvertibleTo nor AssignableTo %v", - toFuzz.Interface(), - argT)) - } - } - } - return f -} - -// Fuzz recursively fills all of obj's fields with something random. -// Not safe for cyclic or tree-like structs! -// obj must be a pointer. Only exported (public) fields can be set (thanks, golang :/ ) -// Intended for tests, so will panic on bad input or unimplemented fields. -func (f *Fuzzer) Fuzz(obj interface{}) { - v := reflect.ValueOf(obj) - if v.Kind() != reflect.Ptr { - panic("needed ptr!") - } - v = v.Elem() - f.doFuzz(v) -} - -func (f *Fuzzer) doFuzz(v reflect.Value) { - if !v.CanSet() { - return - } - // Check for both pointer and non-pointer custom functions. - if v.CanAddr() && f.tryCustom(v.Addr()) { - return - } - if f.tryCustom(v) { - return - } - if fn, ok := fillFuncMap[v.Kind()]; ok { - fn(v) - return - } - switch v.Kind() { - case reflect.Map: - if rand.Intn(5) > 0 { - v.Set(reflect.MakeMap(v.Type())) - n := 1 + rand.Intn(2) - for i := 0; i < n; i++ { - key := reflect.New(v.Type().Key()).Elem() - f.doFuzz(key) - val := reflect.New(v.Type().Elem()).Elem() - f.doFuzz(val) - v.SetMapIndex(key, val) - } - return - } - v.Set(reflect.Zero(v.Type())) - case reflect.Ptr: - if rand.Intn(5) > 0 { - v.Set(reflect.New(v.Type().Elem())) - f.doFuzz(v.Elem()) - return - } - v.Set(reflect.Zero(v.Type())) - case reflect.Slice: - if rand.Intn(5) > 0 { - n := 1 + rand.Intn(2) - v.Set(reflect.MakeSlice(v.Type(), n, n)) - for i := 0; i < n; i++ { - f.doFuzz(v.Index(i)) - } - return - } - v.Set(reflect.Zero(v.Type())) - case reflect.Struct: - for i := 0; i < v.NumField(); i++ { - f.doFuzz(v.Field(i)) - } - case reflect.Array: - fallthrough - case reflect.Chan: - fallthrough - case reflect.Func: - fallthrough - case reflect.Interface: - fallthrough - default: - panic(fmt.Sprintf("Can't handle %#v", v.Interface())) - } -} - -// tryCustom searches for custom handlers, and returns true iff it finds a match -// and successfully randomizes v. -func (f Fuzzer) tryCustom(v reflect.Value) bool { - doCustom, ok := f.customFuzz[v.Type()] - if !ok { - return false - } - - switch v.Kind() { - case reflect.Ptr: - if v.IsNil() { - if !v.CanSet() { - return false - } - v.Set(reflect.New(v.Type().Elem())) - } - case reflect.Map: - if v.IsNil() { - if !v.CanSet() { - return false - } - v.Set(reflect.MakeMap(v.Type())) - } - default: - return false - } - - doCustom(v) - return true -} - -func fuzzInt(v reflect.Value) { - v.SetInt(int64(RandUint64())) -} - -func fuzzUint(v reflect.Value) { - v.SetUint(RandUint64()) -} - -var fillFuncMap = map[reflect.Kind]func(reflect.Value){ - reflect.Bool: func(v reflect.Value) { - v.SetBool(RandBool()) - }, - reflect.Int: fuzzInt, - reflect.Int8: fuzzInt, - reflect.Int16: fuzzInt, - reflect.Int32: fuzzInt, - reflect.Int64: fuzzInt, - reflect.Uint: fuzzUint, - reflect.Uint8: fuzzUint, - reflect.Uint16: fuzzUint, - reflect.Uint32: fuzzUint, - reflect.Uint64: fuzzUint, - reflect.Uintptr: fuzzUint, - reflect.Float32: func(v reflect.Value) { - v.SetFloat(float64(rand.Float32())) - }, - reflect.Float64: func(v reflect.Value) { - v.SetFloat(rand.Float64()) - }, - reflect.Complex64: func(v reflect.Value) { - panic("unimplemented") - }, - reflect.Complex128: func(v reflect.Value) { - panic("unimplemented") - }, - reflect.String: func(v reflect.Value) { - v.SetString(RandString()) - }, - reflect.UnsafePointer: func(v reflect.Value) { - panic("unimplemented") - }, -} - -// RandBool returns true or false randomly. -func RandBool() bool { - if rand.Int()&1 == 1 { - return true - } - return false -} - -type charRange struct { - first, last rune -} - -// choose returns a random unicode character from the given range. -func (r *charRange) choose() rune { - count := int64(r.last - r.first) - return r.first + rune(rand.Int63n(count)) -} - -var unicodeRanges = []charRange{ - {' ', '~'}, // ASCII characters - {'\u00a0', '\u02af'}, // Multi-byte encoded characters - {'\u4e00', '\u9fff'}, // Common CJK (even longer encodings) -} - -// RandString makes a random string up to 20 characters long. The returned string -// may include a variety of (valid) UTF-8 encodings. For testing. -func RandString() string { - n := rand.Intn(20) - runes := make([]rune, n) - for i := range runes { - runes[i] = unicodeRanges[rand.Intn(len(unicodeRanges))].choose() - } - return string(runes) -} - -// RandUint64 makes random 64 bit numbers. -// Weirdly, rand doesn't have a function that gives you 64 random bits. -func RandUint64() uint64 { - return uint64(rand.Uint32())<<32 | uint64(rand.Uint32()) -} diff --git a/pkg/util/fuzz_test.go b/pkg/util/fuzz_test.go deleted file mode 100644 index 8fd321f1e53..00000000000 --- a/pkg/util/fuzz_test.go +++ /dev/null @@ -1,254 +0,0 @@ -/* -Copyright 2014 Google Inc. All rights reserved. - -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 util - -import ( - "reflect" - "testing" -) - -func TestFuzz_basic(t *testing.T) { - obj := &struct { - I int - I8 int8 - I16 int16 - I32 int32 - I64 int64 - U uint - U8 uint8 - U16 uint16 - U32 uint32 - U64 uint64 - Uptr uintptr - S string - B bool - }{} - - failed := map[string]int{} - for i := 0; i < 10; i++ { - NewFuzzer().Fuzz(obj) - - if n, v := "i", obj.I; v == 0 { - failed[n] = failed[n] + 1 - } - if n, v := "i8", obj.I8; v == 0 { - failed[n] = failed[n] + 1 - } - if n, v := "i16", obj.I16; v == 0 { - failed[n] = failed[n] + 1 - } - if n, v := "i32", obj.I32; v == 0 { - failed[n] = failed[n] + 1 - } - if n, v := "i64", obj.I64; v == 0 { - failed[n] = failed[n] + 1 - } - if n, v := "u", obj.U; v == 0 { - failed[n] = failed[n] + 1 - } - if n, v := "u8", obj.U8; v == 0 { - failed[n] = failed[n] + 1 - } - if n, v := "u16", obj.U16; v == 0 { - failed[n] = failed[n] + 1 - } - if n, v := "u32", obj.U32; v == 0 { - failed[n] = failed[n] + 1 - } - if n, v := "u64", obj.U64; v == 0 { - failed[n] = failed[n] + 1 - } - if n, v := "uptr", obj.Uptr; v == 0 { - failed[n] = failed[n] + 1 - } - if n, v := "s", obj.S; v == "" { - failed[n] = failed[n] + 1 - } - if n, v := "b", obj.B; v == false { - failed[n] = failed[n] + 1 - } - } - checkFailed(t, failed) -} - -func checkFailed(t *testing.T, failed map[string]int) { - for k, v := range failed { - if v > 8 { - t.Errorf("%v seems to not be getting set, was zero value %v times", k, v) - } - } -} - -func TestFuzz_structptr(t *testing.T) { - obj := &struct { - A *struct { - S string - } - }{} - - failed := map[string]int{} - for i := 0; i < 10; i++ { - NewFuzzer().Fuzz(obj) - - if n, v := "a", obj.A; v == nil { - failed[n] = failed[n] + 1 - } - if n, v := "as", obj.A; v == nil || v.S == "" { - failed[n] = failed[n] + 1 - } - } - checkFailed(t, failed) -} - -// tryFuzz tries fuzzing up to 20 times. Fail if check() never passes, report the highest -// stage it ever got to. -func tryFuzz(t *testing.T, f *Fuzzer, obj interface{}, check func() (stage int, passed bool)) { - maxStage := 0 - for i := 0; i < 20; i++ { - f.Fuzz(obj) - stage, passed := check() - if stage > maxStage { - maxStage = stage - } - if passed { - return - } - } - t.Errorf("Only ever got to stage %v", maxStage) -} - -func TestFuzz_structmap(t *testing.T) { - obj := &struct { - A map[struct { - S string - }]struct { - S2 string - } - B map[string]string - }{} - - tryFuzz(t, NewFuzzer(), obj, func() (int, bool) { - if obj.A == nil { - return 1, false - } - if len(obj.A) == 0 { - return 2, false - } - for k, v := range obj.A { - if k.S == "" { - return 3, false - } - if v.S2 == "" { - return 4, false - } - } - - if obj.B == nil { - return 5, false - } - if len(obj.B) == 0 { - return 6, false - } - for k, v := range obj.B { - if k == "" { - return 7, false - } - if v == "" { - return 8, false - } - } - return 9, true - }) -} - -func TestFuzz_structslice(t *testing.T) { - obj := &struct { - A []struct { - S string - } - B []string - }{} - - tryFuzz(t, NewFuzzer(), obj, func() (int, bool) { - if obj.A == nil { - return 1, false - } - if len(obj.A) == 0 { - return 2, false - } - for _, v := range obj.A { - if v.S == "" { - return 3, false - } - } - - if obj.B == nil { - return 4, false - } - if len(obj.B) == 0 { - return 5, false - } - for _, v := range obj.B { - if v == "" { - return 6, false - } - } - return 7, true - }) -} - -func TestFuzz_custom(t *testing.T) { - obj := &struct { - A string - B *string - C map[string]string - D *map[string]string - }{} - - testPhrase := "gotcalled" - testMap := map[string]string{"C": "D"} - f := NewFuzzer( - func(s *string) { - *s = testPhrase - }, - func(m map[string]string) { - m["C"] = "D" - }, - ) - - tryFuzz(t, f, obj, func() (int, bool) { - if obj.A != testPhrase { - return 1, false - } - if obj.B == nil { - return 2, false - } - if *obj.B != testPhrase { - return 3, false - } - if e, a := testMap, obj.C; !reflect.DeepEqual(e, a) { - return 4, false - } - if obj.D == nil { - return 5, false - } - if e, a := testMap, *obj.D; !reflect.DeepEqual(e, a) { - return 6, false - } - return 7, true - }) -}