! go test -fuzz=FuzzMinimizeZeroLimitSet -run=FuzzMinimizeZeroLimitSet -fuzztime=10000x -fuzzminimizetime=0x .
! stdout '^ok'
! stdout 'minimizing'
-stdout 'there was an Error'
+stdout -count=1 'there was an Error'
stdout FAIL
# Test that minimization is working for recoverable errors.
! go test -run=FuzzMinimizerRecoverable .
rm testdata
+# Test that minimization is working for recoverable errors. Run it with -v this
+# time to ensure the command line output still looks right.
+! go test -v -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=10000x .
+! stdout '^ok'
+stdout 'got the minimum size!'
+# The error message that was printed should be for the one written to testdata.
+stdout 'contains a non-zero byte of length 50'
+stdout FAIL
+
+# Check that the bytes written to testdata are of length 50 (the minimum size)
+go run ./check_testdata FuzzMinimizerRecoverable 50
+
+# Test that re-running the minimized value causes a crash.
+! go test -run=FuzzMinimizerRecoverable .
+rm testdata
+
# Test that minimization doesn't run for non-recoverable errors.
! go test -fuzz=FuzzMinimizerNonrecoverable -run=FuzzMinimizerNonrecoverable -fuzztime=10000x .
! stdout '^ok'
! stdout 'minimizing'
-stdout 'fuzzing process terminated unexpectedly: exit status 99'
+stdout -count=1 'fuzzing process terminated unexpectedly: exit status 99'
stdout FAIL
# Check that re-running the value causes a crash.
package testing
import (
+ "bytes"
"errors"
"flag"
"fmt"
// 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.
- run := func(e corpusEntry) error {
+ run := func(captureOut io.Writer, e corpusEntry) (ok bool) {
if e.Values == nil {
// The corpusEntry must have non-nil Values in order to run the
// test. If Values is nil, it is a bug in our code.
panic(fmt.Sprintf("corpus file %q was not unmarshaled", e.Path))
}
if shouldFailFast() {
- return nil
+ return true
}
testName := f.name
if e.Path != "" {
},
context: f.testContext,
}
+ if captureOut != nil {
+ // t.parent aliases f.common.
+ t.parent.w = captureOut
+ }
t.w = indenter{&t.common}
if t.chatty != nil {
// TODO(#48132): adjust this to work with test2json.
})
<-t.signal
f.inFuzzFn = false
- if t.Failed() {
- return errors.New(string(f.output))
- }
- return nil
+ return !t.Failed()
}
switch f.fuzzContext.mode {
case fuzzWorker:
// Fuzzing is enabled, and this is a worker process. Follow instructions
// from the coordinator.
- if err := f.fuzzContext.deps.RunFuzzWorker(run); err != nil {
+ if err := f.fuzzContext.deps.RunFuzzWorker(func(e corpusEntry) error {
+ // Don't write to f.w (which points to Stdout) if running from a
+ // fuzz worker. This would become very verbose, particularly during
+ // minimization. Return the error instead, and let the caller deal
+ // with the output.
+ var buf bytes.Buffer
+ if ok := run(&buf, e); !ok {
+ return errors.New(buf.String())
+ }
+ return nil
+ }); 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.
for _, e := range f.corpus {
name := fmt.Sprintf("%s/%s", f.name, filepath.Base(e.Path))
if _, ok, _ := f.testContext.match.fullName(nil, name); ok {
- run(e)
+ run(f.w, e)
}
}
}