Skip to content

Commit

Permalink
testing/quick: generate more map and slice states
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
matttproud authored and bradfitz committed Feb 26, 2016
1 parent 763e8fc commit 0ccabe2
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 35 deletions.
47 changes: 29 additions & 18 deletions src/testing/quick/quick.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand All @@ -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++ {
Expand Down Expand Up @@ -384,3 +393,5 @@ func toString(interfaces []interface{}) string {
}
return strings.Join(s, ", ")
}

func generateNilValue(r *rand.Rand) bool { return r.Intn(20) == 0 }
17 changes: 0 additions & 17 deletions src/testing/quick/quick_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

0 comments on commit 0ccabe2

Please sign in to comment.