]> Cypherpunks repositories - gostls13.git/commitdiff
testing/quick: generate more map and slice states
authorMatt T. Proud <matt.proud@gmail.com>
Thu, 29 Oct 2015 10:14:05 +0000 (11:14 +0100)
committerBrad Fitzpatrick <bradfitz@golang.org>
Fri, 26 Feb 2016 20:04:04 +0000 (20:04 +0000)
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 <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/testing/quick/quick.go
src/testing/quick/quick_test.go

index 187195c75907d7333bda9ab601c2009d4d2222a0..0c2bf2d72b71812e938a58836185d6fcce72024e 100644 (file)
@@ -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 }
index fe443592f87bed611c3957fde67fc56dcb295798..018ece2a5289ed8ae8fd9059096b71a0bc032d95 100644 (file)
@@ -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)
-       }
-}