[!fuzz] skip
[short] skip

# We clean the fuzz cache during this test. Don't clean the user's cache.
env GOCACHE=$WORK/gocache

# Test that fuzzminimizetime cannot be negative seconds
! go test -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=10000x -fuzzminimizetime=-1ms .
! stdout '^ok'
! stdout 'contains a non-zero byte'
stdout 'invalid duration'
stdout FAIL

# Test that fuzzminimizetime cannot be negative times
! go test -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=10000x -fuzzminimizetime=-1x .
! stdout '^ok'
! stdout 'contains a non-zero byte'
stdout 'invalid count'
stdout FAIL

# Test that fuzzminimizetime can be zero seconds, and minimization is disabled
! go test -fuzz=FuzzMinimizeZeroDurationSet -run=FuzzMinimizeZeroDurationSet -fuzztime=10000x -fuzzminimizetime=0s .
! stdout '^ok'
! stdout 'minimizing'
stdout 'there was an Error'
stdout FAIL

# Test that fuzzminimizetime can be zero times, and minimization is disabled
! go test -fuzz=FuzzMinimizeZeroLimitSet -run=FuzzMinimizeZeroLimitSet -fuzztime=10000x -fuzzminimizetime=0x .
! stdout '^ok'
! stdout 'minimizing'
stdout -count=1 'there was an Error'
stdout FAIL

# Test that minimization is working for recoverable errors.
! go test -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 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 -count=1 '^\s+fuzzing process hung or terminated unexpectedly: exit status 99'
stdout FAIL

# Check that re-running the value causes a crash.
! go test -run=FuzzMinimizerNonrecoverable .
rm testdata

# Clear the fuzzing cache. There may already be minimized inputs that would
# interfere with the next stage of the test.
go clean -fuzzcache

# Test that minimization can be cancelled by fuzzminimizetime and the latest
# crash will still be logged and written to testdata.
! go test -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=100x -fuzzminimizetime=1x .
! stdout '^ok'
stdout 'testdata[/\\]fuzz[/\\]FuzzMinimizerRecoverable[/\\]'
! stdout 'got the minimum size!'  # it shouldn't have had enough time to minimize it
stdout FAIL

# Test that re-running the unminimized value causes a crash.
! go test -run=FuzzMinimizerRecoverable .

# TODO(jayconrod,katiehockman): add a test which verifies that the right bytes
# are written to testdata in the case of an interrupt during minimization.

-- go.mod --
module example.com/y

go 1.16
-- y_test.go --
package y

import (
	"os"
	"testing"
)

func FuzzMinimizeZeroDurationSet(f *testing.F) {
	f.Fuzz(func(t *testing.T, b []byte) {
		if len(b) > 5 {
			t.Errorf("there was an Error")
		}
	})
}

func FuzzMinimizeZeroLimitSet(f *testing.F) {
	f.Fuzz(func(t *testing.T, b []byte) {
		if len(b) > 5 {
			t.Errorf("there was an Error")
		}
	})
}

func FuzzMinimizerRecoverable(f *testing.F) {
	f.Add(make([]byte, 100))
	f.Fuzz(func(t *testing.T, b []byte) {
		if len(b) < 50 {
			// Make sure that b is large enough that it can be minimized
			return
		}
		// Given the randomness of the mutations, this should allow the
		// minimizer to trim down the value a bit.
		for _, n := range b {
			if n != 0 {
				if len(b) == 50 {
					t.Log("got the minimum size!")
				}
				t.Fatalf("contains a non-zero byte of length %d", len(b))
			}
		}
	})
}

func FuzzMinimizerNonrecoverable(f *testing.F) {
	f.Fuzz(func(t *testing.T, b []byte) {
		os.Exit(99)
	})
}
-- empty/empty.go --
package empty
-- check_testdata/check_testdata.go --
package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
)

func main() {
	target := os.Args[1]
	numBytes, err := strconv.Atoi(os.Args[2])
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	// Open the file in testdata (there should only be one)
	dir := fmt.Sprintf("testdata/fuzz/%s", target)
	files, err := ioutil.ReadDir(dir)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	if len(files) != 1 {
		fmt.Fprintf(os.Stderr, "expected one file, got %d", len(files))
		os.Exit(1)
	}
	got, err := ioutil.ReadFile(filepath.Join(dir, files[0].Name()))
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	// Trim the newline at the end of the file
	got = bytes.TrimSpace(got)

	// Make sure that there were exactly 100 bytes written to the corpus entry
	prefix := []byte("[]byte(")
	i := bytes.Index(got, prefix)
	gotBytes := got[i+len(prefix) : len(got)-1]
	s, err := strconv.Unquote(string(gotBytes))
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	if want, got := numBytes, len(s); want != got {
		fmt.Fprintf(os.Stderr, "want %d bytes, got %d\n", want, got)
		os.Exit(1)
	}
}
