pkg syscall (openbsd-amd64-cgo), type Timespec struct, Sec int32
pkg testing, func RegisterCover(Cover)
pkg testing, func MainStart(func(string, string) (bool, error), []InternalTest, []InternalBenchmark, []InternalExample) *M
+pkg testing, func MainStart(testDeps, []InternalTest, []InternalBenchmark, []InternalExample) *M
pkg text/template/parse, type DotNode bool
pkg text/template/parse, type Node interface { Copy, String, Type }
pkg unicode, const Version = "6.2.0"
pkg text/template/parse, type CommentNode struct, embedded Pos
pkg text/template/parse, type Mode uint
pkg text/template/parse, type Tree struct, Mode Mode
+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 (*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) 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 (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
\ No newline at end of file
type testFuncs struct {
Tests []testFunc
Benchmarks []testFunc
+ FuzzTargets []testFunc
Examples []testFunc
TestMain *testFunc
Package *Package
}
t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, "", false})
*doImport, *seen = true, true
+ case isTest(name, "Fuzz"):
+ err := checkTestFunc(n, "F")
+ if err != nil {
+ return err
+ }
+ t.FuzzTargets = append(t.FuzzTargets, testFunc{pkg, name, "", false})
}
}
ex := doc.Examples(f)
{{end}}
}
+var fuzzTargets = []testing.InternalFuzzTarget{
+{{range .FuzzTargets}}
+ {"{{.Name}}", {{.Package}}.{{.Name}}},
+{{end}}
+}
+
var examples = []testing.InternalExample{
{{range .Examples}}
{"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}},
CoveredPackages: {{printf "%q" .Covered}},
})
{{end}}
- m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, examples)
+ m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, fuzzTargets, examples)
{{with .TestMain}}
{{.Package}}.{{.Name}}(m)
os.Exit(int(reflect.ValueOf(m).Elem().FieldByName("exitCode").Int()))
"cpu": true,
"cpuprofile": true,
"failfast": true,
+ "fuzz": true,
"list": true,
"memprofile": true,
"memprofilerate": true,
`,
}
+// TODO(katiehockman): complete the testing here
var (
testBench string // -bench flag
testC bool // -c flag
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
cf.String("cpu", "", "")
cf.StringVar(&testCPUProfile, "cpuprofile", "", "")
cf.Bool("failfast", false, "")
+ cf.String("fuzz", "", "")
cf.StringVar(&testList, "list", "", "")
cf.StringVar(&testMemProfile, "memprofile", "", "")
cf.String("memprofilerate", "", "")
// identifiers from other packages (or predeclared identifiers, such as
// "int") and the test file does not include a dot import.
// - The entire test file is the example: the file contains exactly one
-// example function, zero test or benchmark functions, and at least one
-// top-level function, type, variable, or constant declaration other
-// than the example function.
+// example function, zero test, fuzz target, or benchmark function, and at
+// least one top-level function, type, variable, or constant declaration
+// other than the example function.
func Examples(testFiles ...*ast.File) []*Example {
var list []*Example
for _, file := range testFiles {
- hasTests := false // file contains tests or benchmarks
+ hasTests := false // file contains tests, fuzz targets, or benchmarks
numDecl := 0 // number of non-import declarations in the file
var flist []*Example
for _, decl := range file.Decls {
}
numDecl++
name := f.Name.Name
- if isTest(name, "Test") || isTest(name, "Benchmark") {
+ if isTest(name, "Test") || isTest(name, "Benchmark") || isTest(name, "Fuzz") {
hasTests = true
continue
}
return "", false, false // no suitable comment found
}
-// isTest tells whether name looks like a test, example, or benchmark.
-// It is a Test (say) if there is a character after Test that is not a
-// lower-case letter. (We don't want Testiness.)
+// isTest tells whether name looks like a test, example, fuzz target, or
+// benchmark. It is a Test (say) if there is a character after Test that is not
+// a lower-case letter. (We don't want Testiness.)
func isTest(name, prefix string) bool {
if !strings.HasPrefix(name, prefix) {
return false
func (X) BenchmarkFoo() {
}
+func (X) FuzzFoo() {
+}
+
func Example() {
fmt.Println("Hello, world!")
// Output: Hello, world!
func (X) BenchmarkFoo() {
}
+func (X) FuzzFoo() {
+}
+
func main() {
fmt.Println("Hello, world!")
}
--- /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 (
+ "flag"
+ "fmt"
+ "os"
+ "time"
+)
+
+func initFuzzFlags() {
+ matchFuzz = flag.String("test.fuzz", "", "run the fuzz target matching `regexp`")
+}
+
+var matchFuzz *string
+
+// 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
+ context *fuzzContext
+ corpus []corpusEntry // corpus is the in-memory corpus
+ result FuzzResult // result is the result of running the fuzz target
+ fuzzFunc func(f *F) // fuzzFunc is the function which makes up the fuzz target
+ fuzz bool // fuzz indicates whether or not the fuzzing engine should run
+}
+
+// corpus corpusEntry
+type corpusEntry struct {
+ b []byte
+}
+
+// Add will add the arguments to the seed corpus for the fuzz target. This
+// cannot be invoked after or within the Fuzz function. The args must match
+// those in the Fuzz function.
+func (f *F) Add(args ...interface{}) {
+ return
+}
+
+// Fuzz runs the fuzz function, ff, for fuzz testing. It runs ff in a separate
+// goroutine. Only one call to Fuzz is allowed per fuzz target, and any
+// subsequent calls will panic. If ff fails for a set of arguments, those
+// arguments will be added to the seed corpus.
+func (f *F) Fuzz(ff interface{}) {
+ return
+}
+
+// FuzzResult contains the results of a fuzz run.
+type FuzzResult struct {
+ N int // The number of iterations.
+ T time.Duration // The total time taken.
+ Crasher corpusEntry // Crasher is the corpus entry that caused the crash
+ Error error // Error is the error from the crash
+}
+
+func (r FuzzResult) String() string {
+ s := ""
+ if len(r.Error.Error()) != 0 {
+ s = fmt.Sprintf("error: %s\ncrasher: %b", r.Error.Error(), r.Crasher)
+ }
+ return s
+}
+
+// fuzzContext holds all fields that are common to all fuzz targets.
+type fuzzContext struct {
+ runMatch *matcher
+ fuzzMatch *matcher
+}
+
+// RunFuzzTargets is an internal function but exported because it is cross-package;
+// it is part of the implementation of the "go test" command.
+func RunFuzzTargets(matchString func(pat, str string) (bool, error), fuzzTargets []InternalFuzzTarget) (ok bool) {
+ _, ok = runFuzzTargets(matchString, fuzzTargets)
+ return ok
+}
+
+// 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. If -fuzz matches a given fuzz target,
+// then such test will be skipped and run later during fuzzing.
+func runFuzzTargets(matchString func(pat, str string) (bool, error), fuzzTargets []InternalFuzzTarget) (ran, ok bool) {
+ ran, ok = true, true
+ if len(fuzzTargets) == 0 {
+ return false, ok
+ }
+ for _, ft := range fuzzTargets {
+ ctx := &fuzzContext{runMatch: newMatcher(matchString, *match, "-test.run")}
+ f := &F{
+ common: common{
+ signal: make(chan bool),
+ barrier: make(chan bool),
+ w: os.Stdout,
+ name: ft.Name,
+ },
+ context: ctx,
+ }
+ testName, matched, _ := ctx.runMatch.fullName(&f.common, f.name)
+ if !matched {
+ continue
+ }
+ if *matchFuzz != "" {
+ ctx.fuzzMatch = newMatcher(matchString, *matchFuzz, "-test.fuzz")
+ if _, doFuzz, partial := ctx.fuzzMatch.fullName(&f.common, f.name); doFuzz && !partial {
+ continue // this will be run later when fuzzed
+ }
+ }
+ if Verbose() {
+ f.chatty = newChattyPrinter(f.w)
+ }
+ if f.chatty != nil {
+ f.chatty.Updatef(f.name, "=== RUN %s\n", testName)
+ }
+ }
+ return ran, ok
+}
+
+// RunFuzzing is an internal function but exported because it is cross-package;
+// it is part of the implementation of the "go test" command.
+func RunFuzzing(matchString func(pat, str string) (bool, error), fuzzTargets []InternalFuzzTarget) (ok bool) {
+ _, ok = runFuzzing(matchString, fuzzTargets)
+ return ok
+}
+
+// runFuzzing runs the fuzz target matching the pattern for -fuzz. Only one such
+// fuzz target must match. This will run the f.Fuzz function for each seed
+// corpus and will run the fuzzing engine to generate and mutate new inputs
+// against f.Fuzz.
+func runFuzzing(matchString func(pat, str string) (bool, error), fuzzTargets []InternalFuzzTarget) (ran, ok bool) {
+ ran, ok = true, true
+ if len(fuzzTargets) == 0 {
+ return false, ok
+ }
+ ctx := &fuzzContext{
+ fuzzMatch: newMatcher(matchString, *matchFuzz, "-test.fuzz"),
+ }
+ if *matchFuzz == "" {
+ return false, true
+ }
+ f := &F{
+ common: common{
+ signal: make(chan bool),
+ barrier: make(chan bool),
+ w: os.Stdout,
+ },
+ context: ctx,
+ fuzz: true,
+ }
+ var (
+ ft InternalFuzzTarget
+ found int
+ )
+ for _, ft = range fuzzTargets {
+ testName, matched, _ := ctx.fuzzMatch.fullName(&f.common, ft.Name)
+ if matched {
+ found++
+ if found > 1 {
+ fmt.Fprintf(f.w, "testing: warning: -fuzz matched more than one target, won't run\n")
+ return false, ok
+ }
+ f.name = testName
+ }
+ }
+ if Verbose() {
+ f.chatty = newChattyPrinter(f.w)
+ }
+ if f.chatty != nil {
+ f.chatty.Updatef(f.name, "--- FUZZ %s\n", f.name)
+ }
+ return ran, ok
+}
+
+// Fuzz runs a single fuzz target. It is useful for creating
+// custom fuzz targets that do not use the "go test" command.
+//
+// If fn depends on testing flags, then Init must be used to register
+// those flags before calling Fuzz and before calling flag.Parse.
+func Fuzz(fn func(f *F)) FuzzResult {
+ f := &F{
+ common: common{
+ signal: make(chan bool),
+ w: discard{},
+ },
+ fuzzFunc: fn,
+ }
+ // TODO(katiehockman): run the test
+ return f.result
+}
testlog = flag.String("test.testlogfile", "", "write test action log to `file` (for use only by cmd/go)")
initBenchmarkFlags()
+ initFuzzFlags()
}
var (
// new functionality is added to the testing package.
// Systems simulating "go test" should be updated to use MainStart.
func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) {
- os.Exit(MainStart(matchStringOnly(matchString), tests, benchmarks, examples).Run())
+ os.Exit(MainStart(matchStringOnly(matchString), tests, benchmarks, nil, examples).Run())
}
// M is a type passed to a TestMain function to run the actual tests.
type M struct {
- deps testDeps
- tests []InternalTest
- benchmarks []InternalBenchmark
- examples []InternalExample
+ deps testDeps
+ tests []InternalTest
+ benchmarks []InternalBenchmark
+ fuzzTargets []InternalFuzzTarget
+ examples []InternalExample
timer *time.Timer
afterOnce sync.Once
// MainStart is meant for use by tests generated by 'go test'.
// It is not meant to be called directly and is not subject to the Go 1 compatibility document.
// It may change signature from release to release.
-func MainStart(deps testDeps, tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) *M {
+func MainStart(deps testDeps, tests []InternalTest, benchmarks []InternalBenchmark, fuzzTargets []InternalFuzzTarget, examples []InternalExample) *M {
Init()
return &M{
- deps: deps,
- tests: tests,
- benchmarks: benchmarks,
- examples: examples,
+ deps: deps,
+ tests: tests,
+ benchmarks: benchmarks,
+ fuzzTargets: fuzzTargets,
+ examples: examples,
}
}
}
if len(*matchList) != 0 {
- listTests(m.deps.MatchString, m.tests, m.benchmarks, m.examples)
+ listTests(m.deps.MatchString, m.tests, m.benchmarks, m.fuzzTargets, m.examples)
m.exitCode = 0
return
}
deadline := m.startAlarm()
haveExamples = len(m.examples) > 0
testRan, testOk := runTests(m.deps.MatchString, m.tests, deadline)
+ fuzzTargetsRan, fuzzTargetsOk := runFuzzTargets(m.deps.MatchString, m.fuzzTargets)
exampleRan, exampleOk := runExamples(m.deps.MatchString, m.examples)
m.stopAlarm()
- if !testRan && !exampleRan && *matchBenchmarks == "" {
+ if !testRan && !exampleRan && !fuzzTargetsRan && *matchBenchmarks == "" && *matchFuzz == "" {
fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
}
- if !testOk || !exampleOk || !runBenchmarks(m.deps.ImportPath(), m.deps.MatchString, m.benchmarks) || race.Errors() > 0 {
+ if !testOk || !exampleOk || !fuzzTargetsOk || !runBenchmarks(m.deps.ImportPath(), m.deps.MatchString, m.benchmarks) || race.Errors() > 0 {
+ fmt.Println("FAIL")
+ m.exitCode = 1
+ return
+ }
+
+ fuzzingRan, fuzzingOk := runFuzzing(m.deps.MatchString, m.fuzzTargets)
+ if *matchFuzz != "" && !fuzzingRan {
+ fmt.Fprintln(os.Stderr, "testing: warning: no fuzz targets to run")
+ }
+ if !fuzzingOk {
fmt.Println("FAIL")
m.exitCode = 1
return
}
}
-func listTests(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) {
+func listTests(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, fuzzTargets []InternalFuzzTarget, examples []InternalExample) {
if _, err := matchString(*matchList, "non-empty"); err != nil {
fmt.Fprintf(os.Stderr, "testing: invalid regexp in -test.list (%q): %s\n", *matchList, err)
os.Exit(1)
fmt.Println(bench.Name)
}
}
+ for _, fuzzTarget := range fuzzTargets {
+ if ok, _ := matchString(*matchList, fuzzTarget.Name); ok {
+ fmt.Println(fuzzTarget.Name)
+ }
+ }
for _, example := range examples {
if ok, _ := matchString(*matchList, example.Name); ok {
fmt.Println(example.Name)