Skip to content

Commit 0ccabe2

Browse files
matttproudbradfitz
authored andcommitted
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 <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 763e8fc commit 0ccabe2

File tree

2 files changed

+29
-35
lines changed

2 files changed

+29
-35
lines changed

src/testing/quick/quick.go

+29-18
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
"strings"
1515
)
1616

17-
var defaultMaxCount *int = flag.Int("quickchecks", 100, "The default number of iterations for each check")
17+
var defaultMaxCount = flag.Int("quickchecks", 100, "The default number of iterations for each check")
1818

1919
// A Generator can generate random values of its own type.
2020
type Generator interface {
@@ -98,18 +98,22 @@ func sizedValue(t reflect.Type, rand *rand.Rand, size int) (value reflect.Value,
9898
case reflect.Uintptr:
9999
v.SetUint(uint64(randInt64(rand)))
100100
case reflect.Map:
101-
numElems := rand.Intn(size)
102-
v.Set(reflect.MakeMap(concrete))
103-
for i := 0; i < numElems; i++ {
104-
key, ok1 := sizedValue(concrete.Key(), rand, size)
105-
value, ok2 := sizedValue(concrete.Elem(), rand, size)
106-
if !ok1 || !ok2 {
107-
return reflect.Value{}, false
101+
if generateNilValue(rand) {
102+
v.Set(reflect.Zero(concrete)) // Generate nil map.
103+
} else {
104+
numElems := rand.Intn(size)
105+
v.Set(reflect.MakeMap(concrete))
106+
for i := 0; i < numElems; i++ {
107+
key, ok1 := sizedValue(concrete.Key(), rand, size)
108+
value, ok2 := sizedValue(concrete.Elem(), rand, size)
109+
if !ok1 || !ok2 {
110+
return reflect.Value{}, false
111+
}
112+
v.SetMapIndex(key, value)
108113
}
109-
v.SetMapIndex(key, value)
110114
}
111115
case reflect.Ptr:
112-
if rand.Intn(size) == 0 {
116+
if generateNilValue(rand) {
113117
v.Set(reflect.Zero(concrete)) // Generate nil pointer.
114118
} else {
115119
elem, ok := sizedValue(concrete.Elem(), rand, size)
@@ -120,15 +124,20 @@ func sizedValue(t reflect.Type, rand *rand.Rand, size int) (value reflect.Value,
120124
v.Elem().Set(elem)
121125
}
122126
case reflect.Slice:
123-
numElems := rand.Intn(size)
124-
sizeLeft := size - numElems
125-
v.Set(reflect.MakeSlice(concrete, numElems, numElems))
126-
for i := 0; i < numElems; i++ {
127-
elem, ok := sizedValue(concrete.Elem(), rand, sizeLeft)
128-
if !ok {
129-
return reflect.Value{}, false
127+
if generateNilValue(rand) {
128+
v.Set(reflect.Zero(concrete)) // Generate nil slice.
129+
} else {
130+
slCap := rand.Intn(size)
131+
slLen := rand.Intn(slCap + 1)
132+
sizeLeft := size - slCap
133+
v.Set(reflect.MakeSlice(concrete, slLen, slCap))
134+
for i := 0; i < slLen; i++ {
135+
elem, ok := sizedValue(concrete.Elem(), rand, sizeLeft)
136+
if !ok {
137+
return reflect.Value{}, false
138+
}
139+
v.Index(i).Set(elem)
130140
}
131-
v.Index(i).Set(elem)
132141
}
133142
case reflect.Array:
134143
for i := 0; i < v.Len(); i++ {
@@ -384,3 +393,5 @@ func toString(interfaces []interface{}) string {
384393
}
385394
return strings.Join(s, ", ")
386395
}
396+
397+
func generateNilValue(r *rand.Rand) bool { return r.Intn(20) == 0 }

src/testing/quick/quick_test.go

-17
Original file line numberDiff line numberDiff line change
@@ -290,20 +290,3 @@ func TestMutuallyRecursive(t *testing.T) {
290290
f := func(a A) bool { return true }
291291
Check(f, nil)
292292
}
293-
294-
// Some serialization formats (e.g. encoding/pem) cannot distinguish
295-
// between a nil and an empty map or slice, so avoid generating the
296-
// zero value for these.
297-
func TestNonZeroSliceAndMap(t *testing.T) {
298-
type Q struct {
299-
M map[int]int
300-
S []int
301-
}
302-
f := func(q Q) bool {
303-
return q.M != nil && q.S != nil
304-
}
305-
err := Check(f, nil)
306-
if err != nil {
307-
t.Fatal(err)
308-
}
309-
}

0 commit comments

Comments
 (0)