- pkg testing, method (*B) Setenv(string, string)
+pkg compress/lzw, method (*Reader) Close() error
+pkg compress/lzw, method (*Reader) Read([]uint8) (int, error)
+pkg compress/lzw, method (*Reader) Reset(io.Reader, Order, int)
+pkg compress/lzw, method (*Writer) Close() error
+pkg compress/lzw, method (*Writer) Reset(io.Writer, Order, int)
+pkg compress/lzw, method (*Writer) Write([]uint8) (int, error)
+pkg compress/lzw, type Reader struct
+pkg compress/lzw, type Writer struct
+pkg crypto/tls, method (*CertificateRequestInfo) Context() context.Context
+pkg crypto/tls, method (*ClientHelloInfo) Context() context.Context
+pkg crypto/tls, method (*Conn) HandshakeContext(context.Context) error
+pkg debug/elf, const SHT_MIPS_ABIFLAGS = 1879048234
+pkg debug/elf, const SHT_MIPS_ABIFLAGS SectionType
+pkg encoding/csv, method (*Reader) FieldPos(int) (int, int)
+pkg go/ast, method (*FuncDecl) IsMethod() bool
+pkg go/build, type Context struct, ToolTags []string
+pkg go/parser, const SkipObjectResolution = 64
+pkg go/parser, const SkipObjectResolution Mode
+pkg go/types, type Config struct, GoVersion string
+pkg io/fs, func FileInfoToDirEntry(FileInfo) DirEntry
+pkg net, method (*ParseError) Temporary() bool
+pkg net, method (*ParseError) Timeout() bool
+pkg net, method (IP) IsPrivate() bool
+pkg reflect, func VisibleFields(Type) []StructField
+pkg reflect, method (Method) IsExported() bool
+pkg reflect, method (StructField) IsExported() bool
+pkg runtime/cgo (darwin-amd64-cgo), func NewHandle(interface{}) Handle
+pkg runtime/cgo (darwin-amd64-cgo), method (Handle) Delete()
+pkg runtime/cgo (darwin-amd64-cgo), method (Handle) Value() interface{}
+pkg runtime/cgo (darwin-amd64-cgo), type Handle uintptr
+pkg runtime/cgo (freebsd-386-cgo), func NewHandle(interface{}) Handle
+pkg runtime/cgo (freebsd-386-cgo), method (Handle) Delete()
+pkg runtime/cgo (freebsd-386-cgo), method (Handle) Value() interface{}
+pkg runtime/cgo (freebsd-386-cgo), type Handle uintptr
+pkg runtime/cgo (freebsd-amd64-cgo), func NewHandle(interface{}) Handle
+pkg runtime/cgo (freebsd-amd64-cgo), method (Handle) Delete()
+pkg runtime/cgo (freebsd-amd64-cgo), method (Handle) Value() interface{}
+pkg runtime/cgo (freebsd-amd64-cgo), type Handle uintptr
+pkg runtime/cgo (freebsd-arm-cgo), func NewHandle(interface{}) Handle
+pkg runtime/cgo (freebsd-arm-cgo), method (Handle) Delete()
+pkg runtime/cgo (freebsd-arm-cgo), method (Handle) Value() interface{}
+pkg runtime/cgo (freebsd-arm-cgo), type Handle uintptr
+pkg runtime/cgo (linux-386-cgo), func NewHandle(interface{}) Handle
+pkg runtime/cgo (linux-386-cgo), method (Handle) Delete()
+pkg runtime/cgo (linux-386-cgo), method (Handle) Value() interface{}
+pkg runtime/cgo (linux-386-cgo), type Handle uintptr
+pkg runtime/cgo (linux-amd64-cgo), func NewHandle(interface{}) Handle
+pkg runtime/cgo (linux-amd64-cgo), method (Handle) Delete()
+pkg runtime/cgo (linux-amd64-cgo), method (Handle) Value() interface{}
+pkg runtime/cgo (linux-amd64-cgo), type Handle uintptr
+pkg runtime/cgo (linux-arm-cgo), func NewHandle(interface{}) Handle
+pkg runtime/cgo (linux-arm-cgo), method (Handle) Delete()
+pkg runtime/cgo (linux-arm-cgo), method (Handle) Value() interface{}
+pkg runtime/cgo (linux-arm-cgo), type Handle uintptr
+pkg runtime/cgo (netbsd-386-cgo), func NewHandle(interface{}) Handle
+pkg runtime/cgo (netbsd-386-cgo), method (Handle) Delete()
+pkg runtime/cgo (netbsd-386-cgo), method (Handle) Value() interface{}
+pkg runtime/cgo (netbsd-386-cgo), type Handle uintptr
+pkg runtime/cgo (netbsd-amd64-cgo), func NewHandle(interface{}) Handle
+pkg runtime/cgo (netbsd-amd64-cgo), method (Handle) Delete()
+pkg runtime/cgo (netbsd-amd64-cgo), method (Handle) Value() interface{}
+pkg runtime/cgo (netbsd-amd64-cgo), type Handle uintptr
+pkg runtime/cgo (netbsd-arm-cgo), func NewHandle(interface{}) Handle
+pkg runtime/cgo (netbsd-arm-cgo), method (Handle) Delete()
+pkg runtime/cgo (netbsd-arm-cgo), method (Handle) Value() interface{}
+pkg runtime/cgo (netbsd-arm-cgo), type Handle uintptr
+pkg runtime/cgo (netbsd-arm64-cgo), func NewHandle(interface{}) Handle
+pkg runtime/cgo (netbsd-arm64-cgo), method (Handle) Delete()
+pkg runtime/cgo (netbsd-arm64-cgo), method (Handle) Value() interface{}
+pkg runtime/cgo (netbsd-arm64-cgo), type Handle uintptr
+pkg runtime/cgo (openbsd-386-cgo), func NewHandle(interface{}) Handle
+pkg runtime/cgo (openbsd-386-cgo), method (Handle) Delete()
+pkg runtime/cgo (openbsd-386-cgo), method (Handle) Value() interface{}
+pkg runtime/cgo (openbsd-386-cgo), type Handle uintptr
+pkg runtime/cgo (openbsd-amd64-cgo), func NewHandle(interface{}) Handle
+pkg runtime/cgo (openbsd-amd64-cgo), method (Handle) Delete()
+pkg runtime/cgo (openbsd-amd64-cgo), method (Handle) Value() interface{}
+pkg runtime/cgo (openbsd-amd64-cgo), type Handle uintptr
+pkg syscall (openbsd-386), const MSG_CMSG_CLOEXEC = 2048
+pkg syscall (openbsd-386), const MSG_CMSG_CLOEXEC ideal-int
+pkg syscall (openbsd-386-cgo), const MSG_CMSG_CLOEXEC = 2048
+pkg syscall (openbsd-386-cgo), const MSG_CMSG_CLOEXEC ideal-int
+pkg syscall (openbsd-amd64), const MSG_CMSG_CLOEXEC = 2048
+pkg syscall (openbsd-amd64), const MSG_CMSG_CLOEXEC ideal-int
+pkg syscall (openbsd-amd64-cgo), const MSG_CMSG_CLOEXEC = 2048
+pkg syscall (openbsd-amd64-cgo), const MSG_CMSG_CLOEXEC ideal-int
+pkg syscall (windows-386), type SysProcAttr struct, AdditionalInheritedHandles []Handle
+pkg syscall (windows-386), type SysProcAttr struct, ParentProcess Handle
+pkg syscall (windows-amd64), type SysProcAttr struct, AdditionalInheritedHandles []Handle
+pkg syscall (windows-amd64), type SysProcAttr struct, ParentProcess Handle
- >>>>>>> origin/master
+pkg testing, func Fuzz(func(*F)) FuzzResult
+pkg testing, func MainStart(testDeps, []InternalTest, []InternalBenchmark, []InternalFuzzTarget, []InternalExample) *M
+pkg testing, func RunFuzzTargets(func(string, string) (bool, error), []InternalFuzzTarget) bool
+pkg testing, func RunFuzzing(func(string, string) (bool, error), []InternalFuzzTarget) bool
++pkg testing, method (*B) Setenv(string, string)
+pkg testing, method (*F) Add(...interface{})
+pkg testing, method (*F) Cleanup(func())
+pkg testing, method (*F) Error(...interface{})
+pkg testing, method (*F) Errorf(string, ...interface{})
+pkg testing, method (*F) Fail()
+pkg testing, method (*F) FailNow()
+pkg testing, method (*F) Failed() bool
+pkg testing, method (*F) Fatal(...interface{})
+pkg testing, method (*F) Fatalf(string, ...interface{})
+pkg testing, method (*F) Fuzz(interface{})
+pkg testing, method (*F) Helper()
+pkg testing, method (*F) Log(...interface{})
+pkg testing, method (*F) Logf(string, ...interface{})
+pkg testing, method (*F) Name() string
++pkg testing, method (*F) Setenv(string, string)
+pkg testing, method (*F) Skip(...interface{})
+pkg testing, method (*F) SkipNow()
+pkg testing, method (*F) Skipf(string, ...interface{})
+pkg testing, method (*F) Skipped() bool
+pkg testing, method (*F) TempDir() string
+pkg testing, method (*T) Setenv(string, string)
+pkg testing, method (FuzzResult) String() string
+pkg testing, type F struct
+pkg testing, type FuzzResult struct
+pkg testing, type FuzzResult struct, Crasher entry
+pkg testing, type FuzzResult struct, Error error
+pkg testing, type FuzzResult struct, N int
+pkg testing, type FuzzResult struct, T time.Duration
+pkg testing, type InternalFuzzTarget struct
+pkg testing, type InternalFuzzTarget struct, Fn func(*F)
+pkg testing, type InternalFuzzTarget struct, Name string
+pkg text/template/parse, const SkipFuncCheck = 2
+pkg text/template/parse, const SkipFuncCheck Mode
+pkg time, func UnixMicro(int64) Time
+pkg time, func UnixMilli(int64) Time
+pkg time, method (*Time) IsDST() bool
+pkg time, method (Time) UnixMicro() int64
+pkg time, method (Time) UnixMilli() int64
--- /dev/null
+// Copyright 2020 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.
+
+package testing
+
+import (
+ "errors"
+ "flag"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "sync/atomic"
+ "time"
+)
+
+func initFuzzFlags() {
+ matchFuzz = flag.String("test.fuzz", "", "run the fuzz target matching `regexp`")
+ flag.Var(&fuzzDuration, "test.fuzztime", "time to spend fuzzing; default is to run indefinitely")
+ flag.Var(&minimizeDuration, "test.fuzzminimizetime", "time to spend minimizing a value after finding a crash")
+ fuzzCacheDir = flag.String("test.fuzzcachedir", "", "directory where interesting fuzzing inputs are stored")
+ isFuzzWorker = flag.Bool("test.fuzzworker", false, "coordinate with the parent process to fuzz random values")
+}
+
+var (
+ matchFuzz *string
+ fuzzDuration durationOrCountFlag
+ minimizeDuration = durationOrCountFlag{d: 60 * time.Second}
+ fuzzCacheDir *string
+ isFuzzWorker *bool
+
+ // corpusDir is the parent directory of the target's seed corpus within
+ // the package.
+ corpusDir = "testdata/corpus"
+)
+
+// fuzzWorkerExitCode is used as an exit code by fuzz worker processes after an internal error.
+// This distinguishes internal errors from uncontrolled panics and other crashes.
+// Keep in sync with internal/fuzz.workerExitCode.
+const fuzzWorkerExitCode = 70
+
+// InternalFuzzTarget is an internal type but exported because it is cross-package;
+// it is part of the implementation of the "go test" command.
+type InternalFuzzTarget struct {
+ Name string
+ Fn func(f *F)
+}
+
+// F is a type passed to fuzz targets for fuzz testing.
+type F struct {
+ common
+ fuzzContext *fuzzContext
+ testContext *testContext
+ inFuzzFn bool // set to true when fuzz function is running
+ corpus []corpusEntry // corpus is the in-memory corpus
+ result FuzzResult // result is the result of running the fuzz target
+ fuzzCalled bool
+}
+
+var _ TB = (*F)(nil)
+
+// corpusEntry is an alias to the same type as internal/fuzz.CorpusEntry.
+// We use a type alias because we don't want to export this type, and we can't
+// importing internal/fuzz from testing.
+type corpusEntry = struct {
+ Parent string
+ Name string
+ Data []byte
+ Values []interface{}
+ Generation int
+}
+
+// Cleanup registers a function to be called when the test and all its
+// subtests complete. Cleanup functions will be called in last added,
+// first called order.
+func (f *F) Cleanup(fn func()) {
+ if f.inFuzzFn {
+ panic("testing: f.Cleanup was called inside the f.Fuzz function, use t.Cleanup instead")
+ }
+ f.common.Helper()
+ f.common.Cleanup(fn)
+}
+
+// Error is equivalent to Log followed by Fail.
+func (f *F) Error(args ...interface{}) {
+ if f.inFuzzFn {
+ panic("testing: f.Error was called inside the f.Fuzz function, use t.Error instead")
+ }
+ f.common.Helper()
+ f.common.Error(args...)
+}
+
+// Errorf is equivalent to Logf followed by Fail.
+func (f *F) Errorf(format string, args ...interface{}) {
+ if f.inFuzzFn {
+ panic("testing: f.Errorf was called inside the f.Fuzz function, use t.Errorf instead")
+ }
+ f.common.Helper()
+ f.common.Errorf(format, args...)
+}
+
+// Fail marks the function as having failed but continues execution.
+func (f *F) Fail() {
+ if f.inFuzzFn {
+ panic("testing: f.Fail was called inside the f.Fuzz function, use t.Fail instead")
+ }
+ f.common.Helper()
+ f.common.Fail()
+}
+
+// FailNow marks the function as having failed and stops its execution
+// by calling runtime.Goexit (which then runs all deferred calls in the
+// current goroutine).
+// Execution will continue at the next test or benchmark.
+// FailNow must be called from the goroutine running the
+// test or benchmark function, not from other goroutines
+// created during the test. Calling FailNow does not stop
+// those other goroutines.
+func (f *F) FailNow() {
+ if f.inFuzzFn {
+ panic("testing: f.FailNow was called inside the f.Fuzz function, use t.FailNow instead")
+ }
+ f.common.Helper()
+ f.common.FailNow()
+}
+
+// Fatal is equivalent to Log followed by FailNow.
+func (f *F) Fatal(args ...interface{}) {
+ if f.inFuzzFn {
+ panic("testing: f.Fatal was called inside the f.Fuzz function, use t.Fatal instead")
+ }
+ f.common.Helper()
+ f.common.Fatal(args...)
+}
+
+// Fatalf is equivalent to Logf followed by FailNow.
+func (f *F) Fatalf(format string, args ...interface{}) {
+ if f.inFuzzFn {
+ panic("testing: f.Fatalf was called inside the f.Fuzz function, use t.Fatalf instead")
+ }
+ f.common.Helper()
+ f.common.Fatalf(format, args...)
+}
+
+// Helper marks the calling function as a test helper function.
+// When printing file and line information, that function will be skipped.
+// Helper may be called simultaneously from multiple goroutines.
+func (f *F) Helper() {
+ if f.inFuzzFn {
+ panic("testing: f.Helper was called inside the f.Fuzz function, use t.Helper instead")
+ }
+
+ // common.Helper is inlined here.
+ // If we called it, it would mark F.Helper as the helper
+ // instead of the caller.
+ f.mu.Lock()
+ defer f.mu.Unlock()
+ if f.helperPCs == nil {
+ f.helperPCs = make(map[uintptr]struct{})
+ }
+ // repeating code from callerName here to save walking a stack frame
+ var pc [1]uintptr
+ n := runtime.Callers(2, pc[:]) // skip runtime.Callers + Helper
+ if n == 0 {
+ panic("testing: zero callers found")
+ }
+ if _, found := f.helperPCs[pc[0]]; !found {
+ f.helperPCs[pc[0]] = struct{}{}
+ f.helperNames = nil // map will be recreated next time it is needed
+ }
+}
+
++// Setenv is not supported since fuzzing runs in parallel.
++func (f *F) Setenv(key, value string) {
++ panic("testing: f.Setenv is not supported")
++}
++
+// Skip is equivalent to Log followed by SkipNow.
+func (f *F) Skip(args ...interface{}) {
+ if f.inFuzzFn {
+ panic("testing: f.Skip was called inside the f.Fuzz function, use t.Skip instead")
+ }
+ f.common.Helper()
+ f.common.Skip(args...)
+}
+
+// SkipNow marks the test as having been skipped and stops its execution
+// by calling runtime.Goexit.
+// If a test fails (see Error, Errorf, Fail) and is then skipped,
+// it is still considered to have failed.
+// Execution will continue at the next test or benchmark. See also FailNow.
+// SkipNow must be called from the goroutine running the test, not from
+// other goroutines created during the test. Calling SkipNow does not stop
+// those other goroutines.
+func (f *F) SkipNow() {
+ if f.inFuzzFn {
+ panic("testing: f.SkipNow was called inside the f.Fuzz function, use t.SkipNow instead")
+ }
+ f.common.Helper()
+ f.common.SkipNow()
+}
+
+// Skipf is equivalent to Logf followed by SkipNow.
+func (f *F) Skipf(format string, args ...interface{}) {
+ if f.inFuzzFn {
+ panic("testing: f.Skipf was called inside the f.Fuzz function, use t.Skipf instead")
+ }
+ f.common.Helper()
+ f.common.Skipf(format, args...)
+}
+
+// TempDir returns a temporary directory for the test to use.
+// The directory is automatically removed by Cleanup when the test and
+// all its subtests complete.
+// Each subsequent call to t.TempDir returns a unique directory;
+// if the directory creation fails, TempDir terminates the test by calling Fatal.
+func (f *F) TempDir() string {
+ if f.inFuzzFn {
+ panic("testing: f.TempDir was called inside the f.Fuzz function, use t.TempDir instead")
+ }
+ f.common.Helper()
+ return f.common.TempDir()
+}
+
+// Add will add the arguments to the seed corpus for the fuzz target. This will
+// be a no-op if called after or within the Fuzz function. The args must match
+// or be convertible to those in the Fuzz function.
+func (f *F) Add(args ...interface{}) {
+ var values []interface{}
+ for i := range args {
+ if t := reflect.TypeOf(args[i]); !supportedTypes[t] {
+ panic(fmt.Sprintf("testing: unsupported type to Add %v", t))
+ }
+ values = append(values, args[i])
+ }
+ f.corpus = append(f.corpus, corpusEntry{Values: values, Name: fmt.Sprintf("seed#%d", len(f.corpus))})
+}
+
+// supportedTypes represents all of the supported types which can be fuzzed.
+var supportedTypes = map[reflect.Type]bool{
+ reflect.TypeOf(([]byte)("")): true,
+ reflect.TypeOf((string)("")): true,
+ reflect.TypeOf((bool)(false)): true,
+ reflect.TypeOf((byte)(0)): true,
+ reflect.TypeOf((rune)(0)): true,
+ reflect.TypeOf((float32)(0)): true,
+ reflect.TypeOf((float64)(0)): true,
+ reflect.TypeOf((int)(0)): true,
+ reflect.TypeOf((int8)(0)): true,
+ reflect.TypeOf((int16)(0)): true,
+ reflect.TypeOf((int32)(0)): true,
+ reflect.TypeOf((int64)(0)): true,
+ reflect.TypeOf((uint)(0)): true,
+ reflect.TypeOf((uint8)(0)): true,
+ reflect.TypeOf((uint16)(0)): true,
+ reflect.TypeOf((uint32)(0)): true,
+ reflect.TypeOf((uint64)(0)): true,
+}
+
+// Fuzz runs the fuzz function, ff, for fuzz testing. If ff fails for a set of
+// arguments, those arguments will be added to the seed corpus.
+//
+// ff must be a function with no return value whose first argument is *T and
+// whose remaining arguments are the types to be fuzzed.
+// For example:
+//
+// f.Fuzz(func(t *testing.T, b []byte, i int) { ... })
+//
+// This function should be fast, deterministic, and stateless.
+// None of the pointers to any input data should be retained between executions.
+//
+// This is a terminal function which will terminate the currently running fuzz
+// target by calling runtime.Goexit.
+// To run any code after fuzzing stops, use (*F).Cleanup.
+func (f *F) Fuzz(ff interface{}) {
+ if f.fuzzCalled {
+ panic("testing: F.Fuzz called more than once")
+ }
+ f.fuzzCalled = true
+ if f.failed {
+ return
+ }
+ f.Helper()
+
+ // ff should be in the form func(*testing.T, ...interface{})
+ fn := reflect.ValueOf(ff)
+ fnType := fn.Type()
+ if fnType.Kind() != reflect.Func {
+ panic("testing: F.Fuzz must receive a function")
+ }
+ if fnType.NumIn() < 2 || fnType.In(0) != reflect.TypeOf((*T)(nil)) {
+ panic("testing: F.Fuzz function must receive at least two arguments, where the first argument is a *T")
+ }
+
+ // Save the types of the function to compare against the corpus.
+ var types []reflect.Type
+ for i := 1; i < fnType.NumIn(); i++ {
+ t := fnType.In(i)
+ if !supportedTypes[t] {
+ panic(fmt.Sprintf("testing: unsupported type for fuzzing %v", t))
+ }
+ types = append(types, t)
+ }
+
+ // Check the corpus provided by f.Add
+ for _, c := range f.corpus {
+ if err := f.fuzzContext.checkCorpus(c.Values, types); err != nil {
+ // TODO: Is there a way to save which line number is associated
+ // with the f.Add call that failed?
+ f.Fatal(err)
+ }
+ }
+
+ // Load seed corpus
+ c, err := f.fuzzContext.readCorpus(filepath.Join(corpusDir, f.name), types)
+ if err != nil {
+ f.Fatal(err)
+ }
+ f.corpus = append(f.corpus, c...)
+
+ // run calls fn on a given input, as a subtest with its own T.
+ // run is analogous to T.Run. The test filtering and cleanup works similarly.
+ // fn is called in its own goroutine.
+ //
+ // TODO(jayconrod,katiehockman): dedupe testdata corpus with entries from f.Add
+ run := func(e corpusEntry) error {
+ if e.Values == nil {
+ // Every code path should have already unmarshaled Data into Values.
+ // It's our fault if it didn't.
+ panic(fmt.Sprintf("corpus file %q was not unmarshaled", e.Name))
+ }
+ if shouldFailFast() {
+ return nil
+ }
+ testName := f.common.name
+ if e.Name != "" {
+ testName = fmt.Sprintf("%s/%s", testName, e.Name)
+ }
+
+ // Record the stack trace at the point of this call so that if the subtest
+ // function - which runs in a separate stack - is marked as a helper, we can
+ // continue walking the stack into the parent test.
+ var pc [maxStackLen]uintptr
+ n := runtime.Callers(2, pc[:])
+ t := &T{
+ common: common{
+ barrier: make(chan bool),
+ signal: make(chan bool),
+ name: testName,
+ parent: &f.common,
+ level: f.level + 1,
+ creator: pc[:n],
+ chatty: f.chatty,
+ fuzzing: true,
+ },
+ context: f.testContext,
+ }
+ t.w = indenter{&t.common}
+ if t.chatty != nil {
+ t.chatty.Updatef(t.name, "=== RUN %s\n", t.name)
+ }
+ f.inFuzzFn = true
+ go tRunner(t, func(t *T) {
+ args := []reflect.Value{reflect.ValueOf(t)}
+ for _, v := range e.Values {
+ args = append(args, reflect.ValueOf(v))
+ }
+ // Before reseting the current coverage, defer the snapshot so that we
+ // make sure it is called right before the tRunner function exits,
+ // regardless of whether it was executed cleanly, panicked, or if the
+ // fuzzFn called t.Fatal.
+ defer f.fuzzContext.snapshotCoverage()
+ f.fuzzContext.resetCoverage()
+ fn.Call(args)
+ })
+ <-t.signal
+ f.inFuzzFn = false
+ if t.Failed() {
+ return errors.New(string(f.output))
+ }
+ return nil
+ }
+
+ switch {
+ case f.fuzzContext.coordinateFuzzing != nil:
+ // Fuzzing is enabled, and this is the test process started by 'go test'.
+ // Act as the coordinator process, and coordinate workers to perform the
+ // actual fuzzing.
+ corpusTargetDir := filepath.Join(corpusDir, f.name)
+ cacheTargetDir := filepath.Join(*fuzzCacheDir, f.name)
+ err := f.fuzzContext.coordinateFuzzing(
+ fuzzDuration.d,
+ int64(fuzzDuration.n),
+ minimizeDuration.d,
+ int64(minimizeDuration.n),
+ *parallel,
+ f.corpus,
+ types,
+ corpusTargetDir,
+ cacheTargetDir)
+ if err != nil {
+ f.result = FuzzResult{Error: err}
+ f.Fail()
+ fmt.Fprintf(f.w, "%v\n", err)
+ if crashErr, ok := err.(fuzzCrashError); ok {
+ crashName := crashErr.CrashName()
+ fmt.Fprintf(f.w, "Crash written to %s\n", filepath.Join("testdata/corpus", f.name, crashName))
+ fmt.Fprintf(f.w, "To re-run:\ngo test %s -run=%s/%s\n", f.fuzzContext.importPath(), f.name, crashName)
+ }
+ }
+ // TODO(jayconrod,katiehockman): Aggregate statistics across workers
+ // and add to FuzzResult (ie. time taken, num iterations)
+
+ case f.fuzzContext.runFuzzWorker != nil:
+ // Fuzzing is enabled, and this is a worker process. Follow instructions
+ // from the coordinator.
+ if err := f.fuzzContext.runFuzzWorker(run); err != nil {
+ // Internal errors are marked with f.Fail; user code may call this too, before F.Fuzz.
+ // The worker will exit with fuzzWorkerExitCode, indicating this is a failure
+ // (and 'go test' should exit non-zero) but a crasher should not be recorded.
+ f.Errorf("communicating with fuzzing coordinator: %v", err)
+ }
+
+ default:
+ // Fuzzing is not enabled, or will be done later. Only run the seed
+ // corpus now.
+ for _, e := range f.corpus {
+ run(e)
+ }
+ }
+
+ // Record that the fuzz function (or coordinateFuzzing or runFuzzWorker)
+ // returned normally. This is used to distinguish runtime.Goexit below
+ // from panic(nil).
+ f.finished = true
+
+ // Terminate the goroutine. F.Fuzz should not return.
+ // We cannot call runtime.Goexit from a deferred function: if there is a
+ // panic, that would replace the panic value with nil.
+ runtime.Goexit()
+}
+
+func (f *F) report() {
+ if *isFuzzWorker || f.parent == nil {
+ return
+ }
+ dstr := fmtDuration(f.duration)
+ format := "--- %s: %s (%s)\n"
+ if f.Failed() {
+ f.flushToParent(f.name, format, "FAIL", f.name, dstr)
+ } else if f.chatty != nil {
+ if f.Skipped() {
+ f.flushToParent(f.name, format, "SKIP", f.name, dstr)
+ } else {
+ f.flushToParent(f.name, format, "PASS", f.name, dstr)
+ }
+ }
+}
+
+// FuzzResult contains the results of a fuzz run.
+type FuzzResult struct {
+ N int // The number of iterations.
+ T time.Duration // The total time taken.
+ Error error // Error is the error from the crash
+}
+
+func (r FuzzResult) String() string {
+ s := ""
+ if r.Error == nil {
+ return s
+ }
+ s = fmt.Sprintf("%s", r.Error.Error())
+ return s
+}
+
+// fuzzCrashError is satisfied by a crash detected within the fuzz function.
+// These errors are written to the seed corpus and can be re-run with 'go test'.
+// Errors within the fuzzing framework (like I/O errors between coordinator
+// and worker processes) don't satisfy this interface.
+type fuzzCrashError interface {
+ error
+ Unwrap() error
+
+ // CrashName returns the name of the subtest that corresponds to the saved
+ // crash input file in the seed corpus. The test can be re-run with
+ // go test $pkg -run=$target/$name where $pkg is the package's import path,
+ // $target is the fuzz target name, and $name is the string returned here.
+ CrashName() string
+}
+
+// fuzzContext holds all fields that are common to all fuzz targets.
+type fuzzContext struct {
+ importPath func() string
+ coordinateFuzzing func(time.Duration, int64, time.Duration, int64, int, []corpusEntry, []reflect.Type, string, string) error
+ runFuzzWorker func(func(corpusEntry) error) error
+ readCorpus func(string, []reflect.Type) ([]corpusEntry, error)
+ checkCorpus func(vals []interface{}, types []reflect.Type) error
+ resetCoverage func()
+ snapshotCoverage func()
+}
+
+// runFuzzTargets runs the fuzz targets matching the pattern for -run. This will
+// only run the f.Fuzz function for each seed corpus without using the fuzzing
+// engine to generate or mutate inputs.
+func runFuzzTargets(deps testDeps, fuzzTargets []InternalFuzzTarget, deadline time.Time) (ran, ok bool) {
+ ok = true
+ if len(fuzzTargets) == 0 || *isFuzzWorker {
+ return ran, ok
+ }
+ m := newMatcher(deps.MatchString, *match, "-test.run")
+ tctx := newTestContext(*parallel, m)
+ tctx.deadline = deadline
+ fctx := &fuzzContext{
+ importPath: deps.ImportPath,
+ readCorpus: deps.ReadCorpus,
+ checkCorpus: deps.CheckCorpus,
+ resetCoverage: deps.ResetCoverage,
+ snapshotCoverage: deps.SnapshotCoverage,
+ }
+ root := common{w: os.Stdout} // gather output in one place
+ if Verbose() {
+ root.chatty = newChattyPrinter(root.w)
+ }
+ for _, ft := range fuzzTargets {
+ if shouldFailFast() {
+ break
+ }
+ testName, matched, _ := tctx.match.fullName(nil, ft.Name)
+ if !matched {
+ continue
+ }
+ f := &F{
+ common: common{
+ signal: make(chan bool),
+ barrier: make(chan bool),
+ name: testName,
+ parent: &root,
+ level: root.level + 1,
+ chatty: root.chatty,
+ },
+ testContext: tctx,
+ fuzzContext: fctx,
+ }
+ f.w = indenter{&f.common}
+ if f.chatty != nil {
+ f.chatty.Updatef(f.name, "=== RUN %s\n", f.name)
+ }
+
+ go fRunner(f, ft.Fn)
+ <-f.signal
+ }
+ return root.ran, !root.Failed()
+}
+
+// runFuzzing runs the fuzz target matching the pattern for -fuzz. Only one such
+// fuzz target must match. This will run the fuzzing engine to generate and
+// mutate new inputs against the f.Fuzz function.
+//
+// If fuzzing is disabled (-test.fuzz is not set), runFuzzing
+// returns immediately.
+func runFuzzing(deps testDeps, fuzzTargets []InternalFuzzTarget) (ran, ok bool) {
+ // TODO(katiehockman,jayconrod): Should we do something special to make sure
+ // we don't print f.Log statements again with runFuzzing, since we already
+ // would have printed them when we ran runFuzzTargets (ie. seed corpus run)?
+ if len(fuzzTargets) == 0 || *matchFuzz == "" {
+ return false, true
+ }
+ m := newMatcher(deps.MatchString, *matchFuzz, "-test.fuzz")
+ tctx := newTestContext(1, m)
+ fctx := &fuzzContext{
+ importPath: deps.ImportPath,
+ readCorpus: deps.ReadCorpus,
+ checkCorpus: deps.CheckCorpus,
+ resetCoverage: deps.ResetCoverage,
+ snapshotCoverage: deps.SnapshotCoverage,
+ }
+ root := common{w: os.Stdout}
+ if *isFuzzWorker {
+ root.w = io.Discard
+ fctx.runFuzzWorker = deps.RunFuzzWorker
+ } else {
+ fctx.coordinateFuzzing = deps.CoordinateFuzzing
+ }
+ if Verbose() && !*isFuzzWorker {
+ root.chatty = newChattyPrinter(root.w)
+ }
+ var target *InternalFuzzTarget
+ var f *F
+ for i := range fuzzTargets {
+ ft := &fuzzTargets[i]
+ testName, matched, _ := tctx.match.fullName(nil, ft.Name)
+ if !matched {
+ continue
+ }
+ if target != nil {
+ fmt.Fprintln(os.Stderr, "testing: warning: -fuzz matches more than one target, won't fuzz")
+ return false, true
+ }
+ target = ft
+ f = &F{
+ common: common{
+ signal: make(chan bool),
+ barrier: nil, // T.Parallel has no effect when fuzzing.
+ name: testName,
+ parent: &root,
+ level: root.level + 1,
+ chatty: root.chatty,
+ },
+ fuzzContext: fctx,
+ testContext: tctx,
+ }
+ f.w = indenter{&f.common}
+ }
+ if target == nil {
+ return false, true
+ }
+ if f.chatty != nil {
+ f.chatty.Updatef(f.name, "=== FUZZ %s\n", f.name)
+ }
+ go fRunner(f, target.Fn)
+ <-f.signal
+ return f.ran, !f.failed
+}
+
+// fRunner wraps a call to a fuzz target and ensures that cleanup functions are
+// called and status flags are set. fRunner should be called in its own
+// goroutine. To wait for its completion, receive f.signal.
+//
+// fRunner is analogous with tRunner, which wraps subtests started with T.Run.
+// Tests and fuzz targets work a little differently, so for now, these functions
+// aren't consolidated. In particular, because there are no F.Run and F.Parallel
+// methods, i.e., no fuzz sub-targets or parallel fuzz targets, a few
+// simplifications are made. We also require that F.Fuzz, F.Skip, or F.Fail is
+// called.
+func fRunner(f *F, fn func(*F)) {
+ // When this goroutine is done, either because runtime.Goexit was called,
+ // a panic started, or fn returned normally, record the duration and send
+ // t.signal, indicating the fuzz target is done.
+ defer func() {
+ // Detect whether the fuzz target panicked or called runtime.Goexit without
+ // calling F.Fuzz, F.Fail, or F.Skip. If it did, panic (possibly replacing
+ // a nil panic value). Nothing should recover after fRunner unwinds,
+ // so this should crash the process with a stack. Unfortunately, recovering
+ // here adds stack frames, but the location of the original panic should
+ // still be clear.
+ if f.Failed() {
+ atomic.AddUint32(&numFailed, 1)
+ }
+ err := recover()
+ f.mu.RLock()
+ ok := f.skipped || f.failed || (f.fuzzCalled && f.finished)
+ f.mu.RUnlock()
+ if err == nil && !ok {
+ err = errNilPanicOrGoexit
+ }
+
+ // Use a deferred call to ensure that we report that the test is
+ // complete even if a cleanup function calls t.FailNow. See issue 41355.
+ didPanic := false
+ defer func() {
+ if didPanic {
+ return
+ }
+ if err != nil {
+ panic(err)
+ }
+ // Only report that the test is complete if it doesn't panic,
+ // as otherwise the test binary can exit before the panic is
+ // reported to the user. See issue 41479.
+ f.signal <- true
+ }()
+
+ // If we recovered a panic or inappropriate runtime.Goexit, fail the test,
+ // flush the output log up to the root, then panic.
+ doPanic := func(err interface{}) {
+ f.Fail()
+ if r := f.runCleanup(recoverAndReturnPanic); r != nil {
+ f.Logf("cleanup panicked with %v", r)
+ }
+ for root := &f.common; root.parent != nil; root = root.parent {
+ root.mu.Lock()
+ root.duration += time.Since(root.start)
+ d := root.duration
+ root.mu.Unlock()
+ root.flushToParent(root.name, "--- FAIL: %s (%s)\n", root.name, fmtDuration(d))
+ }
+ didPanic = true
+ panic(err)
+ }
+ if err != nil {
+ doPanic(err)
+ }
+
+ // No panic or inappropriate Goexit.
+ f.duration += time.Since(f.start)
+
+ if len(f.sub) > 0 {
+ // Run parallel inputs.
+ // Release the parallel subtests.
+ close(f.barrier)
+ // Wait for the subtests to complete.
+ for _, sub := range f.sub {
+ <-sub.signal
+ }
+ cleanupStart := time.Now()
+ err := f.runCleanup(recoverAndReturnPanic)
+ f.duration += time.Since(cleanupStart)
+ if err != nil {
+ doPanic(err)
+ }
+ }
+
+ // Report after all subtests have finished.
+ f.report()
+ f.done = true
+ f.setRan()
+ }()
+ defer func() {
+ if len(f.sub) == 0 {
+ f.runCleanup(normalPanic)
+ }
+ }()
+
+ f.start = time.Now()
+ fn(f)
+
+ // Code beyond this point is only executed if fn returned normally.
+ // That means fn did not call F.Fuzz or F.Skip. It should have called F.Fail.
+ f.mu.Lock()
+ defer f.mu.Unlock()
+ if !f.failed {
+ panic(f.name + " returned without calling F.Fuzz, F.Fail, or F.Skip")
+ }
+}