aboutsummaryrefslogtreecommitdiff
path: root/src/internal/fuzz/minimize_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/internal/fuzz/minimize_test.go')
-rw-r--r--src/internal/fuzz/minimize_test.go286
1 files changed, 286 insertions, 0 deletions
diff --git a/src/internal/fuzz/minimize_test.go b/src/internal/fuzz/minimize_test.go
new file mode 100644
index 0000000000..410b78310b
--- /dev/null
+++ b/src/internal/fuzz/minimize_test.go
@@ -0,0 +1,286 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build darwin || linux || windows
+// +build darwin linux windows
+
+package fuzz
+
+import (
+ "bytes"
+ "context"
+ "errors"
+ "fmt"
+ "reflect"
+ "testing"
+)
+
+func TestMinimizeInput(t *testing.T) {
+ type testcase struct {
+ name string
+ fn func(CorpusEntry) error
+ input []interface{}
+ expected []interface{}
+ }
+ cases := []testcase{
+ {
+ name: "ones_byte",
+ fn: func(e CorpusEntry) error {
+ b := e.Values[0].([]byte)
+ ones := 0
+ for _, v := range b {
+ if v == 1 {
+ ones++
+ }
+ }
+ if ones == 3 {
+ return fmt.Errorf("bad %v", e.Values[0])
+ }
+ return nil
+ },
+ input: []interface{}{[]byte{0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+ expected: []interface{}{[]byte{1, 1, 1}},
+ },
+ {
+ name: "single_bytes",
+ fn: func(e CorpusEntry) error {
+ b := e.Values[0].([]byte)
+ if len(b) < 2 {
+ return nil
+ }
+ if len(b) == 2 && b[0] == 1 && b[1] == 2 {
+ return nil
+ }
+ return fmt.Errorf("bad %v", e.Values[0])
+ },
+ input: []interface{}{[]byte{1, 2, 3, 4, 5}},
+ expected: []interface{}{[]byte{2, 3}},
+ },
+ {
+ name: "set_of_bytes",
+ fn: func(e CorpusEntry) error {
+ b := e.Values[0].([]byte)
+ if len(b) < 3 {
+ return nil
+ }
+ if bytes.Equal(b, []byte{0, 1, 2, 3, 4, 5}) || bytes.Equal(b, []byte{0, 4, 5}) {
+ return fmt.Errorf("bad %v", e.Values[0])
+ }
+ return nil
+ },
+ input: []interface{}{[]byte{0, 1, 2, 3, 4, 5}},
+ expected: []interface{}{[]byte{0, 4, 5}},
+ },
+ {
+ name: "ones_string",
+ fn: func(e CorpusEntry) error {
+ b := e.Values[0].(string)
+ ones := 0
+ for _, v := range b {
+ if v == '1' {
+ ones++
+ }
+ }
+ if ones == 3 {
+ return fmt.Errorf("bad %v", e.Values[0])
+ }
+ return nil
+ },
+ input: []interface{}{"001010001000000000000000000"},
+ expected: []interface{}{"111"},
+ },
+ {
+ name: "int",
+ fn: func(e CorpusEntry) error {
+ i := e.Values[0].(int)
+ if i > 100 {
+ return fmt.Errorf("bad %v", e.Values[0])
+ }
+ return nil
+ },
+ input: []interface{}{123456},
+ expected: []interface{}{123},
+ },
+ {
+ name: "int8",
+ fn: func(e CorpusEntry) error {
+ i := e.Values[0].(int8)
+ if i > 10 {
+ return fmt.Errorf("bad %v", e.Values[0])
+ }
+ return nil
+ },
+ input: []interface{}{int8(1<<7 - 1)},
+ expected: []interface{}{int8(12)},
+ },
+ {
+ name: "int16",
+ fn: func(e CorpusEntry) error {
+ i := e.Values[0].(int16)
+ if i > 10 {
+ return fmt.Errorf("bad %v", e.Values[0])
+ }
+ return nil
+ },
+ input: []interface{}{int16(1<<15 - 1)},
+ expected: []interface{}{int16(32)},
+ },
+ {
+ fn: func(e CorpusEntry) error {
+ i := e.Values[0].(int32)
+ if i > 10 {
+ return fmt.Errorf("bad %v", e.Values[0])
+ }
+ return nil
+ },
+ input: []interface{}{int32(1<<31 - 1)},
+ expected: []interface{}{int32(21)},
+ },
+ {
+ name: "int32",
+ fn: func(e CorpusEntry) error {
+ i := e.Values[0].(uint)
+ if i > 10 {
+ return fmt.Errorf("bad %v", e.Values[0])
+ }
+ return nil
+ },
+ input: []interface{}{uint(123456)},
+ expected: []interface{}{uint(12)},
+ },
+ {
+ name: "uint8",
+ fn: func(e CorpusEntry) error {
+ i := e.Values[0].(uint8)
+ if i > 10 {
+ return fmt.Errorf("bad %v", e.Values[0])
+ }
+ return nil
+ },
+ input: []interface{}{uint8(1<<8 - 1)},
+ expected: []interface{}{uint8(25)},
+ },
+ {
+ name: "uint16",
+ fn: func(e CorpusEntry) error {
+ i := e.Values[0].(uint16)
+ if i > 10 {
+ return fmt.Errorf("bad %v", e.Values[0])
+ }
+ return nil
+ },
+ input: []interface{}{uint16(1<<16 - 1)},
+ expected: []interface{}{uint16(65)},
+ },
+ {
+ name: "uint32",
+ fn: func(e CorpusEntry) error {
+ i := e.Values[0].(uint32)
+ if i > 10 {
+ return fmt.Errorf("bad %v", e.Values[0])
+ }
+ return nil
+ },
+ input: []interface{}{uint32(1<<32 - 1)},
+ expected: []interface{}{uint32(42)},
+ },
+ {
+ name: "float32",
+ fn: func(e CorpusEntry) error {
+ if i := e.Values[0].(float32); i == 1.23 {
+ return nil
+ }
+ return fmt.Errorf("bad %v", e.Values[0])
+ },
+ input: []interface{}{float32(1.23456789)},
+ expected: []interface{}{float32(1.2)},
+ },
+ {
+ name: "float64",
+ fn: func(e CorpusEntry) error {
+ if i := e.Values[0].(float64); i == 1.23 {
+ return nil
+ }
+ return fmt.Errorf("bad %v", e.Values[0])
+ },
+ input: []interface{}{float64(1.23456789)},
+ expected: []interface{}{float64(1.2)},
+ },
+ }
+
+ // If we are on a 64 bit platform add int64 and uint64 tests
+ if v := int64(1<<63 - 1); int64(int(v)) == v {
+ cases = append(cases, testcase{
+ name: "int64",
+ fn: func(e CorpusEntry) error {
+ i := e.Values[0].(int64)
+ if i > 10 {
+ return fmt.Errorf("bad %v", e.Values[0])
+ }
+ return nil
+ },
+ input: []interface{}{int64(1<<63 - 1)},
+ expected: []interface{}{int64(92)},
+ }, testcase{
+ name: "uint64",
+ fn: func(e CorpusEntry) error {
+ i := e.Values[0].(uint64)
+ if i > 10 {
+ return fmt.Errorf("bad %v", e.Values[0])
+ }
+ return nil
+ },
+ input: []interface{}{uint64(1<<64 - 1)},
+ expected: []interface{}{uint64(18)},
+ })
+ }
+
+ for _, tc := range cases {
+ tc := tc
+ t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
+ ws := &workerServer{
+ fuzzFn: tc.fn,
+ }
+ count := int64(0)
+ vals := tc.input
+ success, err := ws.minimizeInput(context.Background(), vals, &count, 0, nil)
+ if !success {
+ t.Errorf("minimizeInput did not succeed")
+ }
+ if err == nil {
+ t.Fatal("minimizeInput didn't provide an error")
+ }
+ if expected := fmt.Sprintf("bad %v", tc.expected[0]); err.Error() != expected {
+ t.Errorf("unexpected error: got %q, want %q", err, expected)
+ }
+ if !reflect.DeepEqual(vals, tc.expected) {
+ t.Errorf("unexpected results: got %v, want %v", vals, tc.expected)
+ }
+ })
+ }
+}
+
+// TestMinimizeInputCoverageError checks that if we're minimizing an interesting
+// input (one that we don't expect to cause an error), and the fuzz function
+// returns an error, minimizing fails, and we return the error quickly.
+func TestMinimizeInputCoverageError(t *testing.T) {
+ errOhNo := errors.New("ohno")
+ ws := &workerServer{fuzzFn: func(e CorpusEntry) error {
+ return errOhNo
+ }}
+ keepCoverage := make([]byte, len(coverageSnapshot))
+ count := int64(0)
+ vals := []interface{}{[]byte(nil)}
+ success, err := ws.minimizeInput(context.Background(), vals, &count, 0, keepCoverage)
+ if success {
+ t.Error("unexpected success")
+ }
+ if err != errOhNo {
+ t.Errorf("unexpected error: %v", err)
+ }
+ if count != 1 {
+ t.Errorf("count: got %d, want 1", count)
+ }
+}