Skip to content

Commit 23e0799

Browse files
committed
Add some stub fuzzing types/functions to testing
1 parent f3d1543 commit 23e0799

File tree

4 files changed

+181
-11
lines changed

4 files changed

+181
-11
lines changed

src/testing/fuzz.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package testing
2+
3+
// FIXME: These are only the types and functions necessary to compile; they are not properly used or implemented.
4+
5+
import (
6+
"errors"
7+
"fmt"
8+
"reflect"
9+
"time"
10+
)
11+
12+
// InternalFuzzTarget is an internal type but exported because it is
13+
// cross-package; it is part of the implementation of the "go test" command.
14+
type InternalFuzzTarget struct {
15+
Name string
16+
Fn func(f *F)
17+
}
18+
19+
// F is a type passed to fuzz tests.
20+
//
21+
// Fuzz tests run generated inputs against a provided fuzz target, which can
22+
// find and report potential bugs in the code being tested.
23+
//
24+
// A fuzz test runs the seed corpus by default, which includes entries provided
25+
// by (*F).Add and entries in the testdata/fuzz/<FuzzTestName> directory. After
26+
// any necessary setup and calls to (*F).Add, the fuzz test must then call
27+
// (*F).Fuzz to provide the fuzz target. See the testing package documentation
28+
// for an example, and see the F.Fuzz and F.Add method documentation for
29+
// details.
30+
//
31+
// *F methods can only be called before (*F).Fuzz. Once the test is
32+
// executing the fuzz target, only (*T) methods can be used. The only *F methods
33+
// that are allowed in the (*F).Fuzz function are (*F).Failed and (*F).Name.
34+
type F struct {
35+
common
36+
fuzzContext *fuzzContext
37+
testContext *testContext
38+
39+
// inFuzzFn is true when the fuzz function is running. Most F methods cannot
40+
// be called when inFuzzFn is true.
41+
inFuzzFn bool
42+
43+
// corpus is a set of seed corpus entries, added with F.Add and loaded
44+
// from testdata.
45+
corpus []corpusEntry
46+
47+
result fuzzResult
48+
fuzzCalled bool
49+
}
50+
51+
// corpusEntry is an alias to the same type as internal/fuzz.CorpusEntry.
52+
// We use a type alias because we don't want to export this type, and we can't
53+
// import internal/fuzz from testing.
54+
type corpusEntry = struct {
55+
Parent string
56+
Path string
57+
Data []byte
58+
Values []interface{}
59+
Generation int
60+
IsSeed bool
61+
}
62+
63+
// Add will add the arguments to the seed corpus for the fuzz test. This will be
64+
// a no-op if called after or within the fuzz target, and args must match the
65+
// arguments for the fuzz target.
66+
func (f *F) Add(args ...interface{}) {
67+
var values []interface{}
68+
for i := range args {
69+
if t := reflect.TypeOf(args[i]); !supportedTypes[t] {
70+
panic(fmt.Sprintf("testing: unsupported type to Add %v", t))
71+
}
72+
values = append(values, args[i])
73+
}
74+
f.corpus = append(f.corpus, corpusEntry{Values: values, IsSeed: true, Path: fmt.Sprintf("seed#%d", len(f.corpus))})
75+
}
76+
77+
// supportedTypes represents all of the supported types which can be fuzzed.
78+
var supportedTypes = map[reflect.Type]bool{
79+
reflect.TypeOf(([]byte)("")): true,
80+
reflect.TypeOf((string)("")): true,
81+
reflect.TypeOf((bool)(false)): true,
82+
reflect.TypeOf((byte)(0)): true,
83+
reflect.TypeOf((rune)(0)): true,
84+
reflect.TypeOf((float32)(0)): true,
85+
reflect.TypeOf((float64)(0)): true,
86+
reflect.TypeOf((int)(0)): true,
87+
reflect.TypeOf((int8)(0)): true,
88+
reflect.TypeOf((int16)(0)): true,
89+
reflect.TypeOf((int32)(0)): true,
90+
reflect.TypeOf((int64)(0)): true,
91+
reflect.TypeOf((uint)(0)): true,
92+
reflect.TypeOf((uint8)(0)): true,
93+
reflect.TypeOf((uint16)(0)): true,
94+
reflect.TypeOf((uint32)(0)): true,
95+
reflect.TypeOf((uint64)(0)): true,
96+
}
97+
98+
// Fuzz runs the fuzz function, ff, for fuzz testing. If ff fails for a set of
99+
// arguments, those arguments will be added to the seed corpus.
100+
//
101+
// ff must be a function with no return value whose first argument is *T and
102+
// whose remaining arguments are the types to be fuzzed.
103+
// For example:
104+
//
105+
// f.Fuzz(func(t *testing.T, b []byte, i int) { ... })
106+
//
107+
// The following types are allowed: []byte, string, bool, byte, rune, float32,
108+
// float64, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64.
109+
// More types may be supported in the future.
110+
//
111+
// ff must not call any *F methods, e.g. (*F).Log, (*F).Error, (*F).Skip. Use
112+
// the corresponding *T method instead. The only *F methods that are allowed in
113+
// the (*F).Fuzz function are (*F).Failed and (*F).Name.
114+
//
115+
// This function should be fast and deterministic, and its behavior should not
116+
// depend on shared state. No mutatable input arguments, or pointers to them,
117+
// should be retained between executions of the fuzz function, as the memory
118+
// backing them may be mutated during a subsequent invocation. ff must not
119+
// modify the underlying data of the arguments provided by the fuzzing engine.
120+
//
121+
// When fuzzing, F.Fuzz does not return until a problem is found, time runs out
122+
// (set with -fuzztime), or the test process is interrupted by a signal. F.Fuzz
123+
// should be called exactly once, unless F.Skip or F.Fail is called beforehand.
124+
func (f *F) Fuzz(ff interface{}) {
125+
f.failed = true
126+
f.result.N = 0
127+
f.result.T = 0
128+
f.result.Error = errors.New("operation not implemented")
129+
return
130+
}
131+
132+
// fuzzContext holds fields common to all fuzz tests.
133+
type fuzzContext struct {
134+
deps testDeps
135+
mode fuzzMode
136+
}
137+
138+
type fuzzMode uint8
139+
140+
// fuzzResult contains the results of a fuzz run.
141+
type fuzzResult struct {
142+
N int // The number of iterations.
143+
T time.Duration // The total time taken.
144+
Error error // Error is the error from the failing input
145+
}

src/testing/testing.go

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -345,8 +345,9 @@ func newTestContext(m *matcher) *testContext {
345345
// M is a test suite.
346346
type M struct {
347347
// tests is a list of the test names to execute
348-
Tests []InternalTest
349-
Benchmarks []InternalBenchmark
348+
Tests []InternalTest
349+
Benchmarks []InternalBenchmark
350+
fuzzTargets []InternalFuzzTarget
350351

351352
deps testDeps
352353

@@ -359,15 +360,6 @@ type testDeps interface {
359360
MatchString(pat, str string) (bool, error)
360361
}
361362

362-
func MainStart(deps interface{}, tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) *M {
363-
Init()
364-
return &M{
365-
Tests: tests,
366-
Benchmarks: benchmarks,
367-
deps: deps.(testDeps),
368-
}
369-
}
370-
371363
// Run runs the tests. It returns an exit code to pass to os.Exit.
372364
func (m *M) Run() (code int) {
373365
defer func() {

src/testing/testing_go118.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//go:build go1.18
2+
// +build go1.18
3+
4+
package testing
5+
6+
// MainStart is meant for use by tests generated by 'go test'.
7+
// It is not meant to be called directly and is not subject to the Go 1 compatibility document.
8+
// It may change signature from release to release.
9+
func MainStart(deps interface{}, tests []InternalTest, benchmarks []InternalBenchmark, fuzzTargets []InternalFuzzTarget, examples []InternalExample) *M {
10+
Init()
11+
return &M{
12+
Tests: tests,
13+
Benchmarks: benchmarks,
14+
fuzzTargets: fuzzTargets,
15+
deps: deps.(testDeps),
16+
}
17+
}

src/testing/testing_other.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//go:build !go1.18
2+
// +build !go1.18
3+
4+
package testing
5+
6+
// MainStart is meant for use by tests generated by 'go test'.
7+
// It is not meant to be called directly and is not subject to the Go 1 compatibility document.
8+
// It may change signature from release to release.
9+
func MainStart(deps interface{}, tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) *M {
10+
Init()
11+
return &M{
12+
Tests: tests,
13+
Benchmarks: benchmarks,
14+
deps: deps.(testDeps),
15+
}
16+
}

0 commit comments

Comments
 (0)