From: Matt T. Proud Date: Thu, 29 Oct 2015 10:14:05 +0000 (+0100) Subject: testing/quick: generate more map and slice states X-Git-Tag: go1.7beta1~1687 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=0ccabe2e0b42a2602e0f37ce28d5368aa811f530;p=gostls13.git testing/quick: generate more map and slice states This change adds support in testing/quick to generate maps and slices in additional states: (1.) nil maps (2.) nil slices (3.) empty slice occupancy: `len(s) == 0 && s != nil` (4.) partial slice occupancy: `len(s) < cap(s) && s != nil` (5.) full slice occupancy: `len(s) == cap(s) && s != nil` Prior to this, only #5 was ever generated, thereby not sufficiently exercising all of the fuzzable code path outcomes. This change depends on https://go-review.googlesource.com/#/c/17499/. Change-Id: I9343c475cefbd72ffc5237281826465c25872206 Reviewed-on: https://go-review.googlesource.com/16470 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- diff --git a/src/testing/quick/quick.go b/src/testing/quick/quick.go index 187195c759..0c2bf2d72b 100644 --- a/src/testing/quick/quick.go +++ b/src/testing/quick/quick.go @@ -14,7 +14,7 @@ import ( "strings" ) -var defaultMaxCount *int = flag.Int("quickchecks", 100, "The default number of iterations for each check") +var defaultMaxCount = flag.Int("quickchecks", 100, "The default number of iterations for each check") // A Generator can generate random values of its own type. type Generator interface { @@ -98,18 +98,22 @@ func sizedValue(t reflect.Type, rand *rand.Rand, size int) (value reflect.Value, case reflect.Uintptr: v.SetUint(uint64(randInt64(rand))) case reflect.Map: - numElems := rand.Intn(size) - v.Set(reflect.MakeMap(concrete)) - for i := 0; i < numElems; i++ { - key, ok1 := sizedValue(concrete.Key(), rand, size) - value, ok2 := sizedValue(concrete.Elem(), rand, size) - if !ok1 || !ok2 { - return reflect.Value{}, false + if generateNilValue(rand) { + v.Set(reflect.Zero(concrete)) // Generate nil map. + } else { + numElems := rand.Intn(size) + v.Set(reflect.MakeMap(concrete)) + for i := 0; i < numElems; i++ { + key, ok1 := sizedValue(concrete.Key(), rand, size) + value, ok2 := sizedValue(concrete.Elem(), rand, size) + if !ok1 || !ok2 { + return reflect.Value{}, false + } + v.SetMapIndex(key, value) } - v.SetMapIndex(key, value) } case reflect.Ptr: - if rand.Intn(size) == 0 { + if generateNilValue(rand) { v.Set(reflect.Zero(concrete)) // Generate nil pointer. } else { elem, ok := sizedValue(concrete.Elem(), rand, size) @@ -120,15 +124,20 @@ func sizedValue(t reflect.Type, rand *rand.Rand, size int) (value reflect.Value, v.Elem().Set(elem) } case reflect.Slice: - numElems := rand.Intn(size) - sizeLeft := size - numElems - v.Set(reflect.MakeSlice(concrete, numElems, numElems)) - for i := 0; i < numElems; i++ { - elem, ok := sizedValue(concrete.Elem(), rand, sizeLeft) - if !ok { - return reflect.Value{}, false + if generateNilValue(rand) { + v.Set(reflect.Zero(concrete)) // Generate nil slice. + } else { + slCap := rand.Intn(size) + slLen := rand.Intn(slCap + 1) + sizeLeft := size - slCap + v.Set(reflect.MakeSlice(concrete, slLen, slCap)) + for i := 0; i < slLen; i++ { + elem, ok := sizedValue(concrete.Elem(), rand, sizeLeft) + if !ok { + return reflect.Value{}, false + } + v.Index(i).Set(elem) } - v.Index(i).Set(elem) } case reflect.Array: for i := 0; i < v.Len(); i++ { @@ -384,3 +393,5 @@ func toString(interfaces []interface{}) string { } return strings.Join(s, ", ") } + +func generateNilValue(r *rand.Rand) bool { return r.Intn(20) == 0 } diff --git a/src/testing/quick/quick_test.go b/src/testing/quick/quick_test.go index fe443592f8..018ece2a52 100644 --- a/src/testing/quick/quick_test.go +++ b/src/testing/quick/quick_test.go @@ -290,20 +290,3 @@ func TestMutuallyRecursive(t *testing.T) { f := func(a A) bool { return true } Check(f, nil) } - -// Some serialization formats (e.g. encoding/pem) cannot distinguish -// between a nil and an empty map or slice, so avoid generating the -// zero value for these. -func TestNonZeroSliceAndMap(t *testing.T) { - type Q struct { - M map[int]int - S []int - } - f := func(q Q) bool { - return q.M != nil && q.S != nil - } - err := Check(f, nil) - if err != nil { - t.Fatal(err) - } -}