aboutsummaryrefslogtreecommitdiff
path: root/src/testing
diff options
context:
space:
mode:
authorKatie Hockman <katie@golang.org>2021-11-09 17:13:36 -0500
committerKatie Hockman <katie@golang.org>2021-11-11 21:06:07 +0000
commit9d89a5eb64f25ce0e7cc6086d44b6f327cbb302c (patch)
treeb453ae64e87fe65a5c8cc16338d27634553760a9 /src/testing
parentccd41cc05e3ee2f0d0ded1d7faf9c1f43ce1037b (diff)
downloadgo-9d89a5eb64f25ce0e7cc6086d44b6f327cbb302c.tar.gz
go-9d89a5eb64f25ce0e7cc6086d44b6f327cbb302c.zip
all: update terminology for fuzzing
This change doesn't modify any functionality. It also doesn't update all of the comments and variable names of the internal code, but everything user facing should be correct. Updates #49185 Change-Id: Ia8b2c94b89ba45897c4085ea0c17a3d8896f7ec7 Reviewed-on: https://go-review.googlesource.com/c/go/+/362794 Trust: Katie Hockman <katie@golang.org> Run-TryBot: Katie Hockman <katie@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Roland Shoemaker <roland@golang.org>
Diffstat (limited to 'src/testing')
-rw-r--r--src/testing/fuzz.go138
-rw-r--r--src/testing/testing.go57
2 files changed, 99 insertions, 96 deletions
diff --git a/src/testing/fuzz.go b/src/testing/fuzz.go
index 46c9d63df4..24a0080730 100644
--- a/src/testing/fuzz.go
+++ b/src/testing/fuzz.go
@@ -19,9 +19,9 @@ import (
)
func initFuzzFlags() {
- matchFuzz = flag.String("test.fuzz", "", "run the fuzz target matching `regexp`")
+ matchFuzz = flag.String("test.fuzz", "", "run the fuzz test 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")
+ flag.Var(&minimizeDuration, "test.fuzzminimizetime", "time to spend minimizing a value after finding a failing input")
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")
}
@@ -33,34 +33,38 @@ var (
fuzzCacheDir *string
isFuzzWorker *bool
- // corpusDir is the parent directory of the target's seed corpus within
+ // corpusDir is the parent directory of the fuzz test's seed corpus within
// the package.
corpusDir = "testdata/fuzz"
)
-// 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.
+// fuzzWorkerExitCode is used as an exit code by fuzz worker processes after an
+// internal error. This distinguishes internal errors from uncontrolled panics
+// and other failiures. 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.
+// 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.
+// F is a type passed to fuzz tests.
//
-// A fuzz target may add seed corpus entries using F.Add or by storing files in
-// the testdata/fuzz/<FuzzTargetName> directory. The fuzz target must then
-// call F.Fuzz once to provide a fuzz function. See the testing package
-// documentation for an example, and see the F.Fuzz and F.Add method
-// documentation for details.
+// Fuzz tests run generated inputs against a provided fuzz target, which can
+// find and report potential bugs in the code being tested.
//
-// *F methods can only be called before (*F).Fuzz. Once inside the function
-// passed to (*F).Fuzz, only (*T) methods can be used. The only *F methods that
-// are allowed in the (*F).Fuzz function are (*F).Failed and (*F).Name.
+// A fuzz test runs the seed corpus by default, which includes entries provided
+// by (*F).Add and entries in the testdata/fuzz/<FuzzTestName> directory. After
+// any necessary setup and calls to (*F).Add, the fuzz test must then call
+// (*F).Fuzz to provide the fuzz target. See the testing package documentation
+// for an example, and see the F.Fuzz and F.Add method documentation for
+// details.
+//
+// *F methods can only be called before (*F).Fuzz. Once the the test is
+// executing the fuzz target, only (*T) methods can be used. The only *F methods
+// that are allowed in the (*F).Fuzz function are (*F).Failed and (*F).Name.
type F struct {
common
fuzzContext *fuzzContext
@@ -97,7 +101,7 @@ type corpusEntry = struct {
// 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")
+ panic("testing: f.Helper was called inside the fuzz target, use t.Helper instead")
}
// common.Helper is inlined here.
@@ -125,7 +129,7 @@ func (f *F) Fail() {
// (*F).Fail may be called by (*T).Fail, which we should allow. However, we
// shouldn't allow direct (*F).Fail calls from inside the (*F).Fuzz function.
if f.inFuzzFn {
- panic("testing: f.Fail was called inside the f.Fuzz function, use t.Fail instead")
+ panic("testing: f.Fail was called inside the fuzz target, use t.Fail instead")
}
f.common.Helper()
f.common.Fail()
@@ -136,15 +140,15 @@ func (f *F) Skipped() bool {
// (*F).Skipped may be called by tRunner, which we should allow. However, we
// shouldn't allow direct (*F).Skipped calls from inside the (*F).Fuzz function.
if f.inFuzzFn {
- panic("testing: f.Skipped was called inside the f.Fuzz function, use t.Skipped instead")
+ panic("testing: f.Skipped was called inside the fuzz target, use t.Skipped instead")
}
f.common.Helper()
return f.common.Skipped()
}
-// 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
-// those in the Fuzz function.
+// Add will add the arguments to the seed corpus for the fuzz test. This will be
+// a no-op if called after or within the fuzz target, and args must match the
+// arguments for the fuzz target.
func (f *F) Add(args ...interface{}) {
var values []interface{}
for i := range args {
@@ -220,7 +224,7 @@ func (f *F) Fuzz(ff interface{}) {
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")
+ panic("testing: fuzz target must receive at least two arguments, where the first argument is a *T")
}
// Save the types of the function to compare against the corpus.
@@ -354,7 +358,7 @@ func (f *F) Fuzz(ff interface{}) {
fmt.Fprintf(f.w, "%v\n", err)
if crashErr, ok := err.(fuzzCrashError); ok {
crashPath := crashErr.CrashPath()
- fmt.Fprintf(f.w, "Crash written to %s\n", crashPath)
+ fmt.Fprintf(f.w, "Failing input written to %s\n", crashPath)
testName := filepath.Base(crashPath)
fmt.Fprintf(f.w, "To re-run:\ngo test -run=%s/%s\n", f.name, testName)
}
@@ -378,7 +382,7 @@ func (f *F) Fuzz(ff interface{}) {
}); 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.
+ // (and 'go test' should exit non-zero) but a failing input should not be recorded.
f.Errorf("communicating with fuzzing coordinator: %v", err)
}
@@ -415,7 +419,7 @@ func (f *F) report() {
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
+ Error error // Error is the error from the failing input
}
func (r fuzzResult) String() string {
@@ -427,7 +431,7 @@ func (r fuzzResult) String() string {
return s
}
-// fuzzCrashError is satisfied by a crash detected within the fuzz function.
+// fuzzCrashError is satisfied by a failing input detected while fuzzing.
// 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.
@@ -437,12 +441,12 @@ type fuzzCrashError interface {
// CrashPath returns the path of the subtest that corresponds to the saved
// crash input file in the seed corpus. The test can be re-run with go test
- // -run=$target/$name $target is the fuzz target name, and $name is the
+ // -run=$test/$name $test is the fuzz test name, and $name is the
// filepath.Base of the string returned here.
CrashPath() string
}
-// fuzzContext holds fields common to all fuzz targets.
+// fuzzContext holds fields common to all fuzz tests.
type fuzzContext struct {
deps testDeps
mode fuzzMode
@@ -456,12 +460,12 @@ const (
fuzzWorker
)
-// 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) {
+// runFuzzTests runs the fuzz tests 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 runFuzzTests(deps testDeps, fuzzTests []InternalFuzzTarget, deadline time.Time) (ran, ok bool) {
ok = true
- if len(fuzzTargets) == 0 || *isFuzzWorker {
+ if len(fuzzTests) == 0 || *isFuzzWorker {
return ran, ok
}
m := newMatcher(deps.MatchString, *match, "-test.run")
@@ -476,7 +480,7 @@ func runFuzzTargets(deps testDeps, fuzzTargets []InternalFuzzTarget, deadline ti
if Verbose() {
root.chatty = newChattyPrinter(root.w)
}
- for _, ft := range fuzzTargets {
+ for _, ft := range fuzzTests {
if shouldFailFast() {
break
}
@@ -486,7 +490,7 @@ func runFuzzTargets(deps testDeps, fuzzTargets []InternalFuzzTarget, deadline ti
}
if mFuzz != nil {
if _, fuzzMatched, _ := mFuzz.fullName(nil, ft.Name); fuzzMatched {
- // If this target will be fuzzed, then don't run the seed corpus
+ // If this will be fuzzed, then don't run the seed corpus
// right now. That will happen later.
continue
}
@@ -515,17 +519,14 @@ func runFuzzTargets(deps testDeps, fuzzTargets []InternalFuzzTarget, deadline ti
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.
+// runFuzzing runs the fuzz test matching the pattern for -fuzz. Only one such
+// fuzz test must match. This will run the fuzzing engine to generate and
+// mutate new inputs against the fuzz target.
//
// If fuzzing is disabled (-test.fuzz is not set), runFuzzing
// returns immediately.
-func runFuzzing(deps testDeps, fuzzTargets []InternalFuzzTarget) (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 == "" {
+func runFuzzing(deps testDeps, fuzzTests []InternalFuzzTarget) (ok bool) {
+ if len(fuzzTests) == 0 || *matchFuzz == "" {
return true
}
m := newMatcher(deps.MatchString, *matchFuzz, "-test.fuzz")
@@ -544,24 +545,24 @@ func runFuzzing(deps testDeps, fuzzTargets []InternalFuzzTarget) (ok bool) {
if Verbose() && !*isFuzzWorker {
root.chatty = newChattyPrinter(root.w)
}
- var target *InternalFuzzTarget
- var targetName string
+ var fuzzTest *InternalFuzzTarget
+ var testName string
var matched []string
- for i := range fuzzTargets {
- name, ok, _ := tctx.match.fullName(nil, fuzzTargets[i].Name)
+ for i := range fuzzTests {
+ name, ok, _ := tctx.match.fullName(nil, fuzzTests[i].Name)
if !ok {
continue
}
matched = append(matched, name)
- target = &fuzzTargets[i]
- targetName = name
+ fuzzTest = &fuzzTests[i]
+ testName = name
}
if len(matched) == 0 {
- fmt.Fprintln(os.Stderr, "testing: warning: no targets to fuzz")
+ fmt.Fprintln(os.Stderr, "testing: warning: no fuzz tests to fuzz")
return true
}
if len(matched) > 1 {
- fmt.Fprintf(os.Stderr, "testing: will not fuzz, -fuzz matches more than one target: %v\n", matched)
+ fmt.Fprintf(os.Stderr, "testing: will not fuzz, -fuzz matches more than one fuzz test: %v\n", matched)
return false
}
@@ -569,7 +570,7 @@ func runFuzzing(deps testDeps, fuzzTargets []InternalFuzzTarget) (ok bool) {
common: common{
signal: make(chan bool),
barrier: nil, // T.Parallel has no effect when fuzzing.
- name: targetName,
+ name: testName,
parent: &root,
level: root.level + 1,
chatty: root.chatty,
@@ -582,31 +583,32 @@ func runFuzzing(deps testDeps, fuzzTargets []InternalFuzzTarget) (ok bool) {
// TODO(#48132): adjust this to work with test2json.
f.chatty.Updatef(f.name, "=== FUZZ %s\n", f.name)
}
- go fRunner(f, target.Fn)
+ go fRunner(f, fuzzTest.Fn)
<-f.signal
return !f.failed
}
-// fRunner wraps a call to a fuzz target and ensures that cleanup functions are
+// fRunner wraps a call to a fuzz test 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 from f.signal.
//
// fRunner is analogous to 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
+// Unit tests and fuzz tests 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-tests or parallel fuzz tests, 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.
+ // 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 test 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 and print stack. Unfortunately, recovering here
- // adds stack frames, but the location of the original panic should still be
+ // Detect whether the fuzz test 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 and print 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)
@@ -662,7 +664,7 @@ func fRunner(f *F, fn func(*F)) {
if len(f.sub) > 0 {
// Unblock inputs that called T.Parallel while running the seed corpus.
- // This only affects fuzz targets run as normal tests.
+ // This only affects fuzz tests run as normal tests.
// While fuzzing, T.Parallel has no effect, so f.sub is empty, and this
// branch is not taken. f.barrier is nil in that case.
close(f.barrier)
diff --git a/src/testing/testing.go b/src/testing/testing.go
index 2ad2266e2d..3458b46d97 100644
--- a/src/testing/testing.go
+++ b/src/testing/testing.go
@@ -146,11 +146,9 @@
// a function is called with randomly generated inputs to find bugs not
// anticipated by unit tests.
//
-// A fuzz target is a function that declares a set of "seed" inputs by calling
-// F.Add, then provides a fuzz function by calling F.Fuzz. A fuzz target has
-// the form:
-//
+// Functions of the form
// func FuzzXxx(*testing.F)
+// are considered fuzz tests.
//
// For example:
//
@@ -170,35 +168,38 @@
// })
// }
//
-// Seed inputs may be registered by calling F.Add or by storing files in the
-// directory testdata/fuzz/<Name> (where <Name> is the name of the fuzz target)
-// within the package containing the fuzz target. Seed inputs are optional, but
-// the fuzzing engine may find bugs more efficiently when provided with a set
-// of small seed inputs with good code coverage.
-//
-// The fuzz function provided to F.Fuzz must accept a *testing.T parameter,
-// followed by one or more parameters for random inputs. The types of arguments
-// passed to F.Add must be identical to the types of these parameters. The fuzz
-// function may signal that it's found a problem the same way tests do: by
-// calling T.Fail (or any method that calls it like T.Error or T.Fatal) or by
-// panicking.
+// A fuzz test maintains a seed corpus, or a set of inputs which are run by
+// default, and can seed input generation. Seed inputs may be registered by
+// calling (*F).Add or by storing files in the directory testdata/fuzz/<Name>
+// (where <Name> is the name of the fuzz test) within the package containing
+// the fuzz test. Seed inputs are optional, but the fuzzing engine may find
+// bugs more efficiently when provided with a set of small seed inputs with good
+// code coverage. These seed inputs can also serve as regression tests for bugs
+// identified through fuzzing.
+//
+// The function passed to (*F).Fuzz within the fuzz test is considered the fuzz
+// target. A fuzz target must accept a *T parameter, followed by one or more
+// parameters for random inputs. The types of arguments passed to (*F).Add must
+// be identical to the types of these parameters. The fuzz target may signal
+// that it's found a problem the same way tests do: by calling T.Fail (or any
+// method that calls it like T.Error or T.Fatal) or by panicking.
//
// When fuzzing is enabled (by setting the -fuzz flag to a regular expression
-// that matches a specific fuzz target), the fuzz function is called with
-// arguments generated by repeatedly making random changes to the seed inputs.
-// On supported platforms, 'go test' compiles the test executable with fuzzing
+// that matches a specific fuzz test), the fuzz target is called with arguments
+// generated by repeatedly making random changes to the seed inputs. On
+// supported platforms, 'go test' compiles the test executable with fuzzing
// coverage instrumentation. The fuzzing engine uses that instrumentation to
-// find and cache inputs that expand coverage, increasing the liklihood of
-// finding bugs. If the fuzz function finds a problem, the fuzzing engine writes
-// the inputs that caused the problem to a file in the directory
+// find and cache inputs that expand coverage, increasing the likelihood of
+// finding bugs. If the fuzz target fails for a given input, the fuzzing engine
+// writes the inputs that caused the failure to a file in the directory
// testdata/fuzz/<Name> within the package directory. This file later serves as
// a seed input. If the file can't be written at that location (for example,
// because the directory is read-only), the fuzzing engine writes the file to
// the fuzz cache directory within the build cache instead.
//
-// When fuzzing is disabled, the fuzz function is called with the seed inputs
+// When fuzzing is disabled, the fuzz target is called with the seed inputs
// registered with F.Add and seed inputs from testdata/fuzz/<Name>. In this
-// mode, the fuzz target acts much like a regular test, with subtests started
+// mode, the fuzz test acts much like a regular test, with subtests started
// with F.Fuzz instead of T.Run.
//
// TODO(#48255): write and link to documentation that will be helpful to users
@@ -217,7 +218,7 @@
// }
//
// The Skip method of *T can be used in a fuzz target if the input is invalid,
-// but should not be considered a crash. For example:
+// but should not be considered a failing input. For example:
//
// func FuzzJSONMarshalling(f *testing.F) {
// f.Fuzz(func(t *testing.T, b []byte) {
@@ -500,7 +501,7 @@ type common struct {
cleanupName string // Name of the cleanup function.
cleanupPc []uintptr // The stack trace at the point where Cleanup was called.
finished bool // Test function has completed.
- inFuzzFn bool // Whether the fuzz function, if this is one, is running.
+ inFuzzFn bool // Whether the fuzz target, if this is one, is running.
chatty *chattyPrinter // A copy of chattyPrinter, if the chatty flag is set.
bench bool // Whether the current test is a benchmark.
@@ -558,7 +559,7 @@ func Verbose() bool {
func (c *common) checkFuzzFn(name string) {
if c.inFuzzFn {
- panic(fmt.Sprintf("testing: f.%s was called inside the f.Fuzz function, use t.%s instead", name, name))
+ panic(fmt.Sprintf("testing: f.%s was called inside the fuzz target, use t.%s instead", name, name))
}
}
@@ -1687,7 +1688,7 @@ func (m *M) Run() (code int) {
deadline := m.startAlarm()
haveExamples = len(m.examples) > 0
testRan, testOk := runTests(m.deps.MatchString, m.tests, deadline)
- fuzzTargetsRan, fuzzTargetsOk := runFuzzTargets(m.deps, m.fuzzTargets, deadline)
+ fuzzTargetsRan, fuzzTargetsOk := runFuzzTests(m.deps, m.fuzzTargets, deadline)
exampleRan, exampleOk := runExamples(m.deps.MatchString, m.examples)
m.stopAlarm()
if !testRan && !exampleRan && !fuzzTargetsRan && *matchBenchmarks == "" && *matchFuzz == "" {