aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/go/internal/test/test.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/go/internal/test/test.go')
-rw-r--r--src/cmd/go/internal/test/test.go131
1 files changed, 112 insertions, 19 deletions
diff --git a/src/cmd/go/internal/test/test.go b/src/cmd/go/internal/test/test.go
index 198afbf4c3d..8f5d57eff14 100644
--- a/src/cmd/go/internal/test/test.go
+++ b/src/cmd/go/internal/test/test.go
@@ -61,8 +61,8 @@ followed by detailed output for each failed package.
'Go test' recompiles each package along with any files with names matching
the file pattern "*_test.go".
-These additional files can contain test functions, benchmark functions, and
-example functions. See 'go help testfunc' for more.
+These additional files can contain test functions, benchmark functions, fuzz
+targets and example functions. See 'go help testfunc' for more.
Each listed package causes the execution of a separate test binary.
Files whose names begin with "_" (including "_test.go") or "." are ignored.
@@ -132,6 +132,8 @@ variables are unchanged. A cached test result is treated as executing
in no time at all,so a successful package test result will be cached and
reused regardless of -timeout setting.
+Run 'go help fuzz' for details around how the go command handles fuzz targets.
+
In addition to the build flags, the flags handled by 'go test' itself are:
-args
@@ -208,7 +210,8 @@ control the execution of any test:
(for example, -benchtime 100x).
-count n
- Run each test and benchmark n times (default 1).
+ Run each test, benchmark, and fuzz targets' seed corpora n times
+ (default 1).
If -cpu is set, run n times for each GOMAXPROCS value.
Examples are always run once.
@@ -237,36 +240,55 @@ control the execution of any test:
Sets -cover.
-cpu 1,2,4
- Specify a list of GOMAXPROCS values for which the tests or
- benchmarks should be executed. The default is the current value
+ Specify a list of GOMAXPROCS values for which the tests, benchmarks or
+ fuzz targets should be executed. The default is the current value
of GOMAXPROCS.
-failfast
Do not start new tests after the first test failure.
+ -fuzz name
+ Run the fuzz target with the given regexp. Must match exactly one fuzz
+ target. This is an experimental feature.
+
+ -fuzztime t
+ Run enough iterations of the fuzz test to take t, specified as a
+ time.Duration (for example, -fuzztime 1h30s). The default is to run
+ forever.
+ The special syntax Nx means to run the fuzz test N times
+ (for example, -fuzztime 100x).
+
-json
Log verbose output and test results in JSON. This presents the
same information as the -v flag in a machine-readable format.
+ -keepfuzzing
+ Keep running the fuzz target if a crasher is found.
+
-list regexp
- List tests, benchmarks, or examples matching the regular expression.
- No tests, benchmarks or examples will be run. This will only
- list top-level tests. No subtest or subbenchmarks will be shown.
+ List tests, benchmarks, fuzz targets, or examples matching the regular
+ expression. No tests, benchmarks, fuzz targets, or examples will be run.
+ This will only list top-level tests. No subtest or subbenchmarks will be
+ shown.
-parallel n
- Allow parallel execution of test functions that call t.Parallel.
+ Allow parallel execution of test functions that call t.Parallel, and
+ f.Fuzz functions that call t.Parallel when running the seed corpus.
The value of this flag is the maximum number of tests to run
- simultaneously; by default, it is set to the value of GOMAXPROCS.
+ simultaneously. While fuzzing, the value of this flag is the
+ maximum number of workers to run the fuzz function simultaneously,
+ regardless of whether t.Parallel has been called; by default, it is set
+ to the value of GOMAXPROCS.
Note that -parallel only applies within a single test binary.
The 'go test' command may run tests for different packages
in parallel as well, according to the setting of the -p flag
(see 'go help build').
-run regexp
- Run only those tests and examples matching the regular expression.
- For tests, the regular expression is split by unbracketed slash (/)
- characters into a sequence of regular expressions, and each part
- of a test's identifier must match the corresponding element in
+ Run only those tests, examples, and fuzz targets matching the regular
+ expression. For tests, the regular expression is split by unbracketed
+ slash (/) characters into a sequence of regular expressions, and each
+ part of a test's identifier must match the corresponding element in
the sequence, if any. Note that possible parents of matches are
run too, so that -run=X/Y matches and runs and reports the result
of all tests matching X, even those without sub-tests matching Y,
@@ -436,6 +458,10 @@ A benchmark function is one named BenchmarkXxx and should have the signature,
func BenchmarkXxx(b *testing.B) { ... }
+A fuzz target is one named FuzzXxx and should have the signature,
+
+ func FuzzXxx(f *testing.F) { ... }
+
An example function is similar to a test function but, instead of using
*testing.T to report success or failure, prints output to os.Stdout.
If the last comment in the function starts with "Output:" then the output
@@ -475,12 +501,34 @@ Here is another example where the ordering of the output is ignored:
The entire test file is presented as the example when it contains a single
example function, at least one other function, type, variable, or constant
-declaration, and no test or benchmark functions.
+declaration, and no fuzz targets or test or benchmark functions.
See the documentation of the testing package for more information.
`,
}
+var HelpFuzz = &base.Command{
+ UsageLine: "fuzz",
+ Short: "fuzzing",
+ Long: `
+By default, go test will build and run the fuzz targets using the target's seed
+corpus only. Any generated corpora in $GOCACHE that were previously written by
+the fuzzing engine will not be run by default.
+
+When -fuzz is set, the binary will be instrumented for coverage. After all
+tests, examples, benchmark functions, and the seed corpora for all fuzz targets
+have been run, go test will begin to fuzz the specified fuzz target.
+Note that this feature is experimental.
+
+-run can be used for testing a single seed corpus entry for a fuzz target. The
+regular expression value of -run can be in the form $target/$name, where $target
+is the name of the fuzz target, and $name is the name of the file (ignoring file
+extensions) to run. For example, -run=FuzzFoo/497b6f87.
+
+See https://golang.org/s/draft-fuzzing-design for more details.
+`,
+}
+
var (
testBench string // -bench flag
testC bool // -c flag
@@ -489,6 +537,7 @@ var (
testCoverPaths []string // -coverpkg flag
testCoverPkgs []*load.Package // -coverpkg flag
testCoverProfile string // -coverprofile flag
+ testFuzz string // -fuzz flag
testJSON bool // -json flag
testList string // -list flag
testO string // -o flag
@@ -622,6 +671,9 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
if testO != "" && len(pkgs) != 1 {
base.Fatalf("cannot use -o flag with multiple packages")
}
+ if testFuzz != "" && len(pkgs) != 1 {
+ base.Fatalf("cannot use -fuzz flag with multiple packages")
+ }
if testProfile() != "" && len(pkgs) != 1 {
base.Fatalf("cannot use %s flag with multiple packages", testProfile())
}
@@ -632,7 +684,9 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
// to that timeout plus one minute. This is a backup alarm in case
// the test wedges with a goroutine spinning and its background
// timer does not get a chance to fire.
- if testTimeout > 0 {
+ // Don't set this if fuzzing, since it should be able to run
+ // indefinitely.
+ if testTimeout > 0 && testFuzz == "" {
testKillTimeout = testTimeout + 1*time.Minute
}
@@ -782,6 +836,32 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
}
}
+ // Inform the compiler that it should instrument the binary at
+ // build-time when fuzzing is enabled.
+ fuzzFlags := work.FuzzInstrumentFlags()
+ if testFuzz != "" && fuzzFlags != nil {
+ // Don't instrument packages which may affect coverage guidance but are
+ // unlikely to be useful. Most of these are used by the testing or
+ // internal/fuzz concurrently with fuzzing.
+ var fuzzNoInstrument = map[string]bool{
+ "context": true,
+ "internal/fuzz": true,
+ "reflect": true,
+ "runtime": true,
+ "sync": true,
+ "sync/atomic": true,
+ "syscall": true,
+ "testing": true,
+ "time": true,
+ }
+ for _, p := range load.TestPackageList(ctx, pkgOpts, pkgs) {
+ if fuzzNoInstrument[p.ImportPath] {
+ continue
+ }
+ p.Internal.Gcflags = append(p.Internal.Gcflags, fuzzFlags...)
+ }
+ }
+
// Prepare build + run + print actions for all packages being tested.
for _, p := range pkgs {
// sync/atomic import is inserted by the cover tool. See #18486
@@ -1087,6 +1167,8 @@ func declareCoverVars(p *load.Package, files ...string) map[string]*load.CoverVa
}
var noTestsToRun = []byte("\ntesting: warning: no tests to run\n")
+var noTargetsToFuzz = []byte("\ntesting: warning: no targets to fuzz\n")
+var tooManyTargetsToFuzz = []byte("\ntesting: warning: -fuzz matches more than one target, won't fuzz\n")
type runCache struct {
disableCache bool // cache should be disabled for this run
@@ -1134,10 +1216,10 @@ func (c *runCache) builderRunTest(b *work.Builder, ctx context.Context, a *work.
}
var buf bytes.Buffer
- if len(pkgArgs) == 0 || (testBench != "") {
+ if len(pkgArgs) == 0 || testBench != "" || testFuzz != "" {
// Stream test output (no buffering) when no package has
// been given on the command line (implicit current directory)
- // or when benchmarking.
+ // or when benchmarking or fuzzing.
// No change to stdout.
} else {
// If we're only running a single package under test or if parallelism is
@@ -1190,7 +1272,12 @@ func (c *runCache) builderRunTest(b *work.Builder, ctx context.Context, a *work.
testlogArg = []string{"-test.testlogfile=" + a.Objdir + "testlog.txt"}
}
panicArg := "-test.paniconexit0"
- args := str.StringList(execCmd, a.Deps[0].BuiltTarget(), testlogArg, panicArg, testArgs)
+ fuzzArg := []string{}
+ if testFuzz != "" {
+ fuzzCacheDir := filepath.Join(cache.Default().FuzzDir(), a.Package.ImportPath)
+ fuzzArg = []string{"-test.fuzzcachedir=" + fuzzCacheDir}
+ }
+ args := str.StringList(execCmd, a.Deps[0].BuiltTarget(), testlogArg, panicArg, fuzzArg, testArgs)
if testCoverProfile != "" {
// Write coverage to temporary profile, for merging later.
@@ -1283,6 +1370,12 @@ func (c *runCache) builderRunTest(b *work.Builder, ctx context.Context, a *work.
if bytes.HasPrefix(out, noTestsToRun[1:]) || bytes.Contains(out, noTestsToRun) {
norun = " [no tests to run]"
}
+ if bytes.HasPrefix(out, noTargetsToFuzz[1:]) || bytes.Contains(out, noTargetsToFuzz) {
+ norun = " [no targets to fuzz]"
+ }
+ if bytes.HasPrefix(out, tooManyTargetsToFuzz[1:]) || bytes.Contains(out, tooManyTargetsToFuzz) {
+ norun = " [will not fuzz, -fuzz matches more than one target]"
+ }
fmt.Fprintf(cmd.Stdout, "ok \t%s\t%s%s%s\n", a.Package.ImportPath, t, coveragePercentage(out), norun)
c.saveOutput(a)
} else {