diff --git a/fuzz.go b/fuzz.go index 6e99d9d..6b5b464 100644 --- a/fuzz.go +++ b/fuzz.go @@ -35,15 +35,16 @@ type fuzzFuncMap map[reflect.Type]reflect.Value // Fuzzer knows how to fill any object with random fields. type Fuzzer struct { - fuzzFuncs fuzzFuncMap - defaultFuzzFuncs fuzzFuncMap - r *rand.Rand - nilChance float64 - minElements int - maxElements int - maxDepth int - allowUnexportedFields bool - skipFieldPatterns []*regexp.Regexp + fuzzFuncs fuzzFuncMap + defaultFuzzFuncs fuzzFuncMap + r *rand.Rand + nilChance float64 + minElements int + maxElements int + maxDepth int + allowUnexportedFields bool + allowUnsupportedFields bool + skipFieldPatterns []*regexp.Regexp fuzzLock sync.Mutex } @@ -60,13 +61,14 @@ func NewWithSeed(seed int64) *Fuzzer { reflect.TypeOf(&time.Time{}): reflect.ValueOf(fuzzTime), }, - fuzzFuncs: fuzzFuncMap{}, - r: rand.New(rand.NewSource(seed)), - nilChance: .2, - minElements: 1, - maxElements: 10, - maxDepth: 100, - allowUnexportedFields: false, + fuzzFuncs: fuzzFuncMap{}, + r: rand.New(rand.NewSource(seed)), + nilChance: .2, + minElements: 1, + maxElements: 10, + maxDepth: 100, + allowUnexportedFields: false, + allowUnsupportedFields: false, } return f } @@ -198,6 +200,13 @@ func (f *Fuzzer) AllowUnexportedFields(flag bool) *Fuzzer { return f } +// AllowUnsupportedFields decides whether to ignore fields that we don't support fuzzing on, +// like interfaces and channels, by default this is not allowed and the fuzzer will panic on an unsupported field. +func (f *Fuzzer) AllowUnsupportedFields(flag bool) *Fuzzer { + f.allowUnsupportedFields = flag + return f +} + // SkipFieldsWithPattern Skip fields which match the supplied pattern. Call this multiple times if needed // This is useful to skip XXX_ fields generated by protobuf func (f *Fuzzer) SkipFieldsWithPattern(pattern *regexp.Regexp) *Fuzzer { @@ -358,7 +367,9 @@ func (fc *fuzzerContext) doFuzz(v reflect.Value, flags uint64) { case reflect.Interface: fallthrough default: - panic(fmt.Sprintf("Can't handle type %v with value %#v", v.Type(), v.Interface())) + if !fc.fuzzer.allowUnsupportedFields { + panic(fmt.Sprintf("Can't handle type %v with value %#v", v.Type(), v.Interface())) + } } } diff --git a/fuzz_test.go b/fuzz_test.go index 18c3fc4..208f1d8 100644 --- a/fuzz_test.go +++ b/fuzz_test.go @@ -511,6 +511,44 @@ func TestFuzzer_AllowUnexportedFields(t *testing.T) { } } +func TestFuzzer_AllowUnsupportedFields(t *testing.T) { + type S struct { + InterfaceField interface{} + } + + f := New().NilChance(0) + obj := S{} + + failingFuzz := func(obj *S) { + defer func() { + if r := recover(); r != nil { + t.Log("Panicked as expected") + } + }() + f.Fuzz(obj) + t.Errorf("Expected to panic") + } + + failingFuzz(&obj) + if obj.InterfaceField != nil { + t.Errorf("Expected obj.InterfaceField to be empty") + } + + f.AllowUnsupportedFields(true) + obj = S{} + f.Fuzz(&obj) + if obj.InterfaceField != nil { + t.Errorf("Expected obj.InterfaceField to be empty") + } + + f.AllowUnsupportedFields(false) + obj = S{} + failingFuzz(&obj) + if obj.InterfaceField != nil { + t.Errorf("Expected obj.InterfaceField to be empty") + } +} + func TestFuzz_SkipPattern(t *testing.T) { obj := &struct { S1 string