Skip to content

Commit a31b142

Browse files
committed
Add a shim for internal/fuzz
This simply shadows the real code temporarily to see what else is broken. It only defines a single type to fix testing/internal/testdeps.
1 parent 5c7fd1d commit a31b142

File tree

9 files changed

+414
-10
lines changed

9 files changed

+414
-10
lines changed

loader/goroot.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ func pathsToOverride(needsSyscallPackage bool) map[string]bool {
230230
"device/": false,
231231
"examples/": false,
232232
"internal/": true,
233+
"internal/fuzz/": false,
233234
"internal/bytealg/": false,
234235
"internal/reflectlite/": false,
235236
"internal/task/": false,

main_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ func TestBuild(t *testing.T) {
6666
"stdlib.go",
6767
"string.go",
6868
"structs.go",
69-
"testing.go",
7069
"zeroalloc.go",
7170
}
7271
_, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
@@ -76,6 +75,11 @@ func TestBuild(t *testing.T) {
7675
if minor >= 17 {
7776
tests = append(tests, "go1.17.go")
7877
}
78+
if minor >= 18 {
79+
tests = append(tests, "testing_go1.18.go")
80+
} else {
81+
tests = append(tests, "testing_other.go")
82+
}
7983

8084
if *testTarget != "" {
8185
// This makes it possible to run one specific test (instead of all),

src/internal/fuzz/fuzz.go

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// Package fuzz is a shim to allow compilation against Go 1.18.
2+
// It only defines a single type to work with testing/internal/testdeps,
3+
// and hide all the other new dependencies from this package.
4+
package fuzz
5+
6+
import (
7+
"context"
8+
"errors"
9+
"io"
10+
"reflect"
11+
"time"
12+
)
13+
14+
// CorpusEntry represents an individual input for fuzzing.
15+
//
16+
// We must use an equivalent type in the testing and testing/internal/testdeps
17+
// packages, but testing can't import this package directly, and we don't want
18+
// to export this type from testing. Instead, we use the same struct type and
19+
// use a type alias (not a defined type) for convenience.
20+
type CorpusEntry = struct {
21+
Parent string
22+
23+
// Path is the path of the corpus file, if the entry was loaded from disk.
24+
// For other entries, including seed values provided by f.Add, Path is the
25+
// name of the test, e.g. seed#0 or its hash.
26+
Path string
27+
28+
// Data is the raw input data. Data should only be populated for seed
29+
// values. For on-disk corpus files, Data will be nil, as it will be loaded
30+
// from disk using Path.
31+
Data []byte
32+
33+
// Values is the unmarshaled values from a corpus file.
34+
Values []any
35+
36+
Generation int
37+
38+
// IsSeed indicates whether this entry is part of the seed corpus.
39+
IsSeed bool
40+
}
41+
42+
// CoordinateFuzzingOpts is a set of arguments for CoordinateFuzzing.
43+
// The zero value is valid for each field unless specified otherwise.
44+
type CoordinateFuzzingOpts struct {
45+
// Log is a writer for logging progress messages and warnings.
46+
// If nil, io.Discard will be used instead.
47+
Log io.Writer
48+
49+
// Timeout is the amount of wall clock time to spend fuzzing after the corpus
50+
// has loaded. If zero, there will be no time limit.
51+
Timeout time.Duration
52+
53+
// Limit is the number of random values to generate and test. If zero,
54+
// there will be no limit on the number of generated values.
55+
Limit int64
56+
57+
// MinimizeTimeout is the amount of wall clock time to spend minimizing
58+
// after discovering a crasher. If zero, there will be no time limit. If
59+
// MinimizeTimeout and MinimizeLimit are both zero, then minimization will
60+
// be disabled.
61+
MinimizeTimeout time.Duration
62+
63+
// MinimizeLimit is the maximum number of calls to the fuzz function to be
64+
// made while minimizing after finding a crash. If zero, there will be no
65+
// limit. Calls to the fuzz function made when minimizing also count toward
66+
// Limit. If MinimizeTimeout and MinimizeLimit are both zero, then
67+
// minimization will be disabled.
68+
MinimizeLimit int64
69+
70+
// parallel is the number of worker processes to run in parallel. If zero,
71+
// CoordinateFuzzing will run GOMAXPROCS workers.
72+
Parallel int
73+
74+
// Seed is a list of seed values added by the fuzz target with testing.F.Add
75+
// and in testdata.
76+
Seed []CorpusEntry
77+
78+
// Types is the list of types which make up a corpus entry.
79+
// Types must be set and must match values in Seed.
80+
Types []reflect.Type
81+
82+
// CorpusDir is a directory where files containing values that crash the
83+
// code being tested may be written. CorpusDir must be set.
84+
CorpusDir string
85+
86+
// CacheDir is a directory containing additional "interesting" values.
87+
// The fuzzer may derive new values from these, and may write new values here.
88+
CacheDir string
89+
}
90+
91+
// CoordinateFuzzing creates several worker processes and communicates with
92+
// them to test random inputs that could trigger crashes and expose bugs.
93+
// The worker processes run the same binary in the same directory with the
94+
// same environment variables as the coordinator process. Workers also run
95+
// with the same arguments as the coordinator, except with the -test.fuzzworker
96+
// flag prepended to the argument list.
97+
//
98+
// If a crash occurs, the function will return an error containing information
99+
// about the crash, which can be reported to the user.
100+
func CoordinateFuzzing(ctx context.Context, opts CoordinateFuzzingOpts) (err error) {
101+
return errors.New("not implemented")
102+
}
103+
104+
// ReadCorpus reads the corpus from the provided dir. The returned corpus
105+
// entries are guaranteed to match the given types. Any malformed files will
106+
// be saved in a MalformedCorpusError and returned, along with the most recent
107+
// error.
108+
func ReadCorpus(dir string, types []reflect.Type) ([]CorpusEntry, error) {
109+
return nil, errors.New("not implemented")
110+
}
111+
112+
// CheckCorpus verifies that the types in vals match the expected types
113+
// provided.
114+
func CheckCorpus(vals []any, types []reflect.Type) error {
115+
return errors.New("not implemented")
116+
}
117+
118+
func ResetCoverage() {}
119+
func SnapshotCoverage() {}
120+
121+
// RunFuzzWorker is called in a worker process to communicate with the
122+
// coordinator process in order to fuzz random inputs. RunFuzzWorker loops
123+
// until the coordinator tells it to stop.
124+
//
125+
// fn is a wrapper on the fuzz function. It may return an error to indicate
126+
// a given input "crashed". The coordinator will also record a crasher if
127+
// the function times out or terminates the process.
128+
//
129+
// RunFuzzWorker returns an error if it could not communicate with the
130+
// coordinator process.
131+
func RunFuzzWorker(ctx context.Context, fn func(CorpusEntry) error) error {
132+
return errors.New("not implemented")
133+
}

src/testing/fuzz.go

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

src/testing/testing.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -430,15 +430,6 @@ type testDeps interface {
430430
MatchString(pat, str string) (bool, error)
431431
}
432432

433-
func MainStart(deps interface{}, tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) *M {
434-
Init()
435-
return &M{
436-
Tests: tests,
437-
Benchmarks: benchmarks,
438-
deps: deps.(testDeps),
439-
}
440-
}
441-
442433
// Run runs the tests. It returns an exit code to pass to os.Exit.
443434
func (m *M) Run() (code int) {
444435
defer func() {

src/testing/testing_go118.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, fuzzTargets []InternalFuzzTarget, examples []InternalExample) *M {
10+
Init()
11+
return &M{
12+
Tests: tests,
13+
Benchmarks: benchmarks,
14+
deps: deps.(testDeps),
15+
}
16+
}

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)