]> git.feebdaed.xyz Git - 0xmirror/go.git/commitdiff
cmd/internal/fuzztest: move fuzz tests out of cmd/go test suite
authorMichael Matloob <matloob@golang.org>
Thu, 4 Dec 2025 19:21:44 +0000 (14:21 -0500)
committerGopher Robot <gobot@golang.org>
Thu, 4 Dec 2025 23:05:53 +0000 (15:05 -0800)
They are very slow: taking them out of the cmd/go test suite makes the
go command tests go from about 80 seconds to run on my system, to about
50 seconds to run.

Change-Id: I19b5c252bd2b6e6d64821cada961ddaa6a6a6964
Reviewed-on: https://go-review.googlesource.com/c/go/+/726960
Auto-Submit: Michael Matloob <matloob@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
Reviewed-by: Michael Matloob <matloob@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

66 files changed:
src/cmd/go/internal/test/test.go
src/cmd/go/testdata/script/test_fuzz.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_cache.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_cgo.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_chatty.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_cleanup.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_context.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_cov.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_deadline.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_dup_cache.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_err_deadlock.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_fuzztime.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_io_error.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_limit_dup_entry.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_match.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_minimize.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_minimize_dirty_cov.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_multiple.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_mutate_crash.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_mutate_fail.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_mutator.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_mutator_repeat.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_non_crash_signal.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_parallel.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_profile_flags.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_return.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_run.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_seed_corpus.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_setenv.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_test_race.txt [deleted file]
src/cmd/go/testdata/script/test_fuzz_unsupported.txt [deleted file]
src/cmd/internal/fuzztest/script_test.go [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/README [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_cache.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_cgo.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_chatty.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_cleanup.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_context.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_cov.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_deadline.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_dup_cache.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_err_deadlock.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_fuzztime.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_io_error.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_limit_dup_entry.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_match.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_minimize.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_minimize_dirty_cov.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_minimize_interesting.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_multiple.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_mutate_crash.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_mutate_fail.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_mutator.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_mutator_repeat.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_non_crash_signal.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_parallel.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_profile_flags.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_return.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_run.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_seed_corpus.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_setenv.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_test_race.txt [new file with mode: 0644]
src/cmd/internal/fuzztest/testdata/script/test_fuzz_unsupported.txt [new file with mode: 0644]
src/cmd/internal/script/scripttest/run.go

index b30b2abc0e9ef7a4e67e47c3e5da96c192089d0a..ddc9c81baf6c068907b2dc2bcf4cb687477dffad 100644 (file)
@@ -736,7 +736,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
                // Otherwise, if fuzzing identifies a failure it could corrupt checksums in
                // the module cache (or permanently alter the behavior of std tests for all
                // users) by writing the failing input to the package's testdata directory.
-               // (See https://golang.org/issue/48495 and test_fuzz_modcache.txt.)
+               // (See https://golang.org/issue/48495 and cmd/internal/fuzztest/test_fuzz_modcache.txt.)
                mainMods := moduleLoaderState.MainModules
                if m := pkgs[0].Module; m != nil && m.Path != "" {
                        if !mainMods.Contains(m.Path) {
diff --git a/src/cmd/go/testdata/script/test_fuzz.txt b/src/cmd/go/testdata/script/test_fuzz.txt
deleted file mode 100644 (file)
index bb88ead..0000000
+++ /dev/null
@@ -1,503 +0,0 @@
-[!fuzz] skip
-[short] skip
-env GOCACHE=$WORK/cache
-
-# This test uses -vet=off to suppress vet, as vet's "tests" analyzer would
-# otherwise statically report the problems we are trying to observe dynamically.
-
-# Test that running a fuzz target that returns without failing or calling
-# f.Fuzz fails and causes a non-zero exit status.
-! go test -vet=off noop_fuzz_test.go
-! stdout ^ok
-stdout FAIL
-
-# Test that fuzzing a fuzz target that returns without failing or calling
-# f.Fuzz fails and causes a non-zero exit status.
-! go test -vet=off -fuzz=Fuzz -fuzztime=1x noop_fuzz_test.go
-! stdout ^ok
-stdout FAIL
-
-# Test that calling f.Error in a fuzz target causes a non-zero exit status.
-! go test -vet=off -fuzz=Fuzz -fuzztime=1x error_fuzz_test.go
-! stdout ^ok
-stdout FAIL
-
-# Test that calling f.Fatal in a fuzz target causes a non-zero exit status.
-! go test -vet=off fatal_fuzz_test.go
-! stdout ^ok
-stdout FAIL
-
-# Test that successful test exits cleanly.
-go test -vet=off success_fuzz_test.go
-stdout ^ok
-! stdout FAIL
-
-# Test that successful fuzzing exits cleanly.
-go test -vet=off -fuzz=Fuzz -fuzztime=1x success_fuzz_test.go
-stdout ok
-! stdout FAIL
-
-# Test that calling f.Fatal while fuzzing causes a non-zero exit status.
-! go test -vet=off -fuzz=Fuzz -fuzztime=1x fatal_fuzz_test.go
-! stdout ^ok
-stdout FAIL
-
-# Test error with seed corpus in f.Fuzz
-! go test -vet=off -run Fuzz_error -vet=off fuzz_add_test.go
-! stdout ^ok
-stdout FAIL
-stdout 'error here'
-
-[short] stop
-
-# Test that calling panic(nil) in a fuzz target causes a non-zero exit status.
-! go test -vet=off panic_fuzz_test.go
-! stdout ^ok
-stdout FAIL
-
-# Test that skipped test exits cleanly.
-go test -vet=off skipped_fuzz_test.go
-stdout ok
-! stdout FAIL
-
-# Test that f.Fatal within f.Fuzz panics
-! go test -vet=off fatal_fuzz_fn_fuzz_test.go
-! stdout ^ok
-! stdout 'fatal here'
-stdout FAIL
-stdout 'fuzz target'
-
-# Test that f.Error within f.Fuzz panics
-! go test -vet=off error_fuzz_fn_fuzz_test.go
-! stdout ^ok
-! stdout 'error here'
-stdout FAIL
-stdout 'fuzz target'
-
-# Test that f.Fail within f.Fuzz panics
-! go test -vet=off fail_fuzz_fn_fuzz_test.go
-! stdout ^ok
-stdout FAIL
-stdout 'fuzz target'
-
-# Test that f.Skip within f.Fuzz panics
-! go test -vet=off skip_fuzz_fn_fuzz_test.go
-! stdout ^ok
-! stdout 'skip here'
-stdout FAIL
-stdout 'fuzz target'
-
-# Test that f.Skipped within f.Fuzz panics
-! go test -vet=off skipped_fuzz_fn_fuzz_test.go
-! stdout ^ok
-! stdout 'f.Skipped is'
-stdout FAIL
-stdout 'fuzz target'
-stdout 't.Skipped is false'
-
-# Test that runtime.Goexit within the fuzz function is an error.
-! go test -vet=off goexit_fuzz_fn_fuzz_test.go
-! stdout ^ok
-stdout FAIL
-
-# Test that a call to f.Fatal after the Fuzz func is executed.
-! go test -vet=off fatal_after_fuzz_func_fuzz_test.go
-! stdout ok
-stdout FAIL
-
-# Test that missing *T in f.Fuzz causes a non-zero exit status.
-! go test -vet=off incomplete_fuzz_call_fuzz_test.go
-! stdout ^ok
-stdout FAIL
-
-# Test that a panic in the Cleanup func is executed.
-! go test -vet=off cleanup_fuzz_test.go
-! stdout ^ok
-stdout FAIL
-stdout 'failed some precondition'
-
-# Test success with seed corpus in f.Fuzz
-go test -vet=off -run Fuzz_pass -vet=off fuzz_add_test.go
-stdout ok
-! stdout FAIL
-! stdout 'off by one error'
-
-# Test fatal with seed corpus in f.Fuzz
-! go test -vet=off -run Fuzz_fatal -vet=off fuzz_add_test.go
-! stdout ^ok
-stdout FAIL
-stdout 'fatal here'
-
-# Test panic with seed corpus in f.Fuzz
-! go test -vet=off -run Fuzz_panic -vet=off fuzz_add_test.go
-! stdout ^ok
-stdout FAIL
-stdout 'off by one error'
-
-# Test panic(nil) with seed corpus in f.Fuzz
-! go test -vet=off -run Fuzz_nilPanic -vet=off fuzz_add_test.go
-! stdout ^ok
-stdout FAIL
-
-# Test panic with unsupported seed corpus
-! go test -vet=off -run Fuzz_unsupported -vet=off fuzz_add_test.go
-! stdout ^ok
-stdout FAIL
-
-# Test panic with different number of args to f.Add
-! go test -vet=off -run Fuzz_addDifferentNumber -vet=off fuzz_add_test.go
-! stdout ^ok
-stdout FAIL
-
-# Test panic with different type of args to f.Add
-! go test -vet=off -run Fuzz_addDifferentType -vet=off fuzz_add_test.go
-! stdout ^ok
-stdout FAIL
-
-# Test that the wrong type given with f.Add will fail.
-! go test -vet=off -run Fuzz_wrongType -vet=off fuzz_add_test.go
-! stdout ^ok
-stdout '\[string int\], want \[\[\]uint8 int8\]'
-stdout FAIL
-
-# Test fatal with testdata seed corpus
-! go test -vet=off -run Fuzz_fail corpustesting/fuzz_testdata_corpus_test.go
-! stdout ^ok
-stdout FAIL
-stdout 'fatal here'
-
-# Test pass with testdata seed corpus
-go test -vet=off -run Fuzz_pass corpustesting/fuzz_testdata_corpus_test.go
-stdout ok
-! stdout FAIL
-! stdout 'fatal here'
-
-# Test pass with testdata and f.Add seed corpus
-go test -vet=off -run Fuzz_passString corpustesting/fuzz_testdata_corpus_test.go
-stdout ok
-! stdout FAIL
-
-# Fuzzing pass with testdata and f.Add seed corpus (skip running tests first)
-go test -vet=off -run=None -fuzz=Fuzz_passString corpustesting/fuzz_testdata_corpus_test.go -fuzztime=10x
-stdout ok
-! stdout FAIL
-
-# Fuzzing pass with testdata and f.Add seed corpus
-go test -vet=off -run=Fuzz_passString -fuzz=Fuzz_passString corpustesting/fuzz_testdata_corpus_test.go -fuzztime=10x
-stdout ok
-! stdout FAIL
-
-# Test panic with malformed seed corpus
-! go test -vet=off -run Fuzz_fail corpustesting/fuzz_testdata_corpus_test.go
-! stdout ^ok
-stdout FAIL
-
-# Test pass with file in other nested testdata directory
-go test -vet=off -run Fuzz_inNestedDir corpustesting/fuzz_testdata_corpus_test.go
-stdout ok
-! stdout FAIL
-! stdout 'fatal here'
-
-# Test fails with file containing wrong type
-! go test -vet=off -run Fuzz_wrongType corpustesting/fuzz_testdata_corpus_test.go
-! stdout ^ok
-stdout FAIL
-
--- noop_fuzz_test.go --
-package noop_fuzz
-
-import "testing"
-
-func Fuzz(f *testing.F) {}
-
--- error_fuzz_test.go --
-package error_fuzz
-
-import "testing"
-
-func Fuzz(f *testing.F) {
-    f.Error("error in target")
-}
-
--- fatal_fuzz_test.go --
-package fatal_fuzz
-
-import "testing"
-
-func Fuzz(f *testing.F) {
-    f.Fatal("fatal in target")
-}
-
--- panic_fuzz_test.go --
-package panic_fuzz
-
-import "testing"
-
-func Fuzz_panic(f *testing.F) {
-    panic(nil)
-}
-
--- success_fuzz_test.go --
-package success_fuzz
-
-import "testing"
-
-func Fuzz(f *testing.F) {
-    f.Fuzz(func (*testing.T, []byte) {})
-}
-
--- skipped_fuzz_test.go --
-package skipped_fuzz
-
-import "testing"
-
-func Fuzz(f *testing.F) {
-    f.Skip()
-}
-
--- fatal_fuzz_fn_fuzz_test.go --
-package fatal_fuzz_fn_fuzz
-
-import "testing"
-
-func Fuzz(f *testing.F) {
-    f.Add([]byte("aa"))
-    f.Fuzz(func(t *testing.T, b []byte) {
-        f.Fatal("fatal here")
-    })
-}
-
--- error_fuzz_fn_fuzz_test.go --
-package error_fuzz_fn_fuzz
-
-import "testing"
-
-func Fuzz(f *testing.F) {
-    f.Add([]byte("aa"))
-    f.Fuzz(func(t *testing.T, b []byte) {
-        f.Error("error here")
-    })
-}
-
--- fail_fuzz_fn_fuzz_test.go --
-package skip_fuzz_fn_fuzz
-
-import "testing"
-
-func Fuzz(f *testing.F) {
-    f.Add([]byte("aa"))
-    f.Fuzz(func(t *testing.T, b []byte) {
-        f.Fail()
-    })
-}
-
--- skip_fuzz_fn_fuzz_test.go --
-package skip_fuzz_fn_fuzz
-
-import "testing"
-
-func Fuzz(f *testing.F) {
-    f.Add([]byte("aa"))
-    f.Fuzz(func(t *testing.T, b []byte) {
-        f.Skip("skip here")
-    })
-}
-
--- skipped_fuzz_fn_fuzz_test.go --
-package skipped_fuzz_fn_fuzz
-
-import "testing"
-
-func Fuzz(f *testing.F) {
-    f.Add([]byte("aa"))
-    f.Fuzz(func(t *testing.T, b []byte) {
-        t.Logf("t.Skipped is %t\n", t.Skipped())
-        t.Logf("f.Skipped is %t\n", f.Skipped())
-    })
-}
-
--- goexit_fuzz_fn_fuzz_test.go --
-package goexit_fuzz_fn_fuzz
-
-import "testing"
-
-func Fuzz(f *testing.F) {
-    f.Add([]byte("aa"))
-    f.Fuzz(func(t *testing.T, b []byte) {
-        runtime.Goexit()
-    })
-}
-
--- fatal_after_fuzz_func_fuzz_test.go --
-package fatal_after_fuzz_func_fuzz
-
-import "testing"
-
-func Fuzz(f *testing.F) {
-    f.Fuzz(func(t *testing.T, b []byte) {
-        // no-op
-    })
-    f.Fatal("this shouldn't be called")
-}
-
--- incomplete_fuzz_call_fuzz_test.go --
-package incomplete_fuzz_call_fuzz
-
-import "testing"
-
-func Fuzz(f *testing.F) {
-    f.Fuzz(func(b []byte) {
-        // this is missing *testing.T as the first param, so should panic
-    })
-}
-
--- cleanup_fuzz_test.go --
-package cleanup_fuzz_test
-
-import "testing"
-
-func Fuzz(f *testing.F) {
-    f.Cleanup(func() {
-        panic("failed some precondition")
-    })
-    f.Fuzz(func(t *testing.T, b []byte) {
-        // no-op
-    })
-}
-
--- fuzz_add_test.go --
-package fuzz_add
-
-import "testing"
-
-func add(f *testing.F) {
-    f.Helper()
-    f.Add([]byte("123"))
-    f.Add([]byte("12345"))
-    f.Add([]byte(""))
-}
-
-func Fuzz_pass(f *testing.F) {
-    add(f)
-    f.Fuzz(func(t *testing.T, b []byte) {
-        if len(b) == -1 {
-            t.Fatal("fatal here") // will not be executed
-        }
-    })
-}
-
-func Fuzz_error(f *testing.F) {
-    add(f)
-    f.Fuzz(func(t *testing.T, b []byte) {
-        if len(b) == 3 {
-            t.Error("error here")
-        }
-    })
-}
-
-func Fuzz_fatal(f *testing.F) {
-    add(f)
-    f.Fuzz(func(t *testing.T, b []byte) {
-        if len(b) == 0 {
-            t.Fatal("fatal here")
-        }
-    })
-}
-
-func Fuzz_panic(f *testing.F) {
-    add(f)
-    f.Fuzz(func(t *testing.T, b []byte) {
-        if len(b) == 5 {
-            panic("off by one error")
-        }
-    })
-}
-
-func Fuzz_nilPanic(f *testing.F) {
-    add(f)
-    f.Fuzz(func(t *testing.T, b []byte) {
-        if len(b) == 3 {
-            panic(nil)
-        }
-    })
-}
-
-func Fuzz_unsupported(f *testing.F) {
-    m := make(map[string]bool)
-    f.Add(m)
-    f.Fuzz(func(*testing.T, []byte) {})
-}
-
-func Fuzz_addDifferentNumber(f *testing.F) {
-    f.Add([]byte("a"))
-    f.Add([]byte("a"), []byte("b"))
-    f.Fuzz(func(*testing.T, []byte) {})
-}
-
-func Fuzz_addDifferentType(f *testing.F) {
-    f.Add(false)
-    f.Add(1234)
-    f.Fuzz(func(*testing.T, []byte) {})
-}
-
-func Fuzz_wrongType(f *testing.F) {
-    f.Add("hello", 50)
-    f.Fuzz(func(*testing.T, []byte, int8) {})
-}
-
--- corpustesting/fuzz_testdata_corpus_test.go --
-package fuzz_testdata_corpus
-
-import "testing"
-
-func fuzzFn(f *testing.F) {
-    f.Helper()
-    f.Fuzz(func(t *testing.T, b []byte) {
-        if string(b) == "12345" {
-            t.Fatal("fatal here")
-        }
-    })
-}
-
-func Fuzz_fail(f *testing.F) {
-    fuzzFn(f)
-}
-
-func Fuzz_pass(f *testing.F) {
-    fuzzFn(f)
-}
-
-func Fuzz_passString(f *testing.F) {
-    f.Add("some seed corpus")
-    f.Fuzz(func(*testing.T, string) {})
-}
-
-func Fuzz_panic(f *testing.F) {
-    f.Fuzz(func(t *testing.T, b []byte) {})
-}
-
-func Fuzz_inNestedDir(f *testing.F) {
-    f.Fuzz(func(t *testing.T, b []byte) {})
-}
-
-func Fuzz_wrongType(f *testing.F) {
-    f.Fuzz(func(t *testing.T, b []byte) {})
-}
-
--- corpustesting/testdata/fuzz/Fuzz_fail/1 --
-go test fuzz v1
-[]byte("12345")
--- corpustesting/testdata/fuzz/Fuzz_pass/1 --
-go test fuzz v1
-[]byte("00000")
--- corpustesting/testdata/fuzz/Fuzz_passString/1 --
-go test fuzz v1
-string("hello")
--- corpustesting/testdata/fuzz/Fuzz_panic/1 --
-malformed
--- corpustesting/testdata/fuzz/Fuzz_inNestedDir/anotherdir/1 --
-go test fuzz v1
-[]byte("12345")
--- corpustesting/testdata/fuzz/Fuzz_wrongType/1 --
-go test fuzz v1
-int("00000")
diff --git a/src/cmd/go/testdata/script/test_fuzz_cache.txt b/src/cmd/go/testdata/script/test_fuzz_cache.txt
deleted file mode 100644 (file)
index 752ab3a..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-[!fuzz-instrumented] skip
-
-[short] skip
-env GOCACHE=$WORK/cache
-
-# Fuzz cache should not exist after a regular test run.
-go test .
-exists $GOCACHE
-! exists $GOCACHE/fuzz
-
-# Fuzzing should write interesting values to the cache.
-go test -fuzz=FuzzY -fuzztime=100x .
-go run ./contains_files $GOCACHE/fuzz/example.com/y/FuzzY
-
-# 'go clean -cache' should not delete the fuzz cache.
-go clean -cache
-exists $GOCACHE/fuzz
-
-# 'go clean -fuzzcache' should delete the fuzz cache but not the build cache.
-go build -x ./empty
-stderr '(compile|gccgo)( |\.exe).*empty.go'
-go clean -fuzzcache
-! exists $GOCACHE/fuzz
-go build -x ./empty
-! stderr '(compile|gccgo)( |\.exe).*empty.go'
-
-# Fuzzing indicates that one new interesting value was found with an empty
-# corpus, and the total size of the cache is now 1.
-go clean -fuzzcache
-go test -fuzz=FuzzEmpty -fuzztime=10000x .
-stdout 'new interesting: 1'
-stdout 'total: 1'
-
-# Fuzzing again with a small fuzztime does not find any other interesting
-# values but still indicates that the cache size is 1.
-go test -fuzz=FuzzEmpty -fuzztime=2x .
-stdout 'new interesting: 0'
-stdout 'total: 1'
-
-! go clean -fuzzcache example.com/y
-stderr 'go: clean -fuzzcache cannot be used with package arguments'
-
--- go.mod --
-module example.com/y
-
-go 1.16
--- y_test.go --
-package y
-
-import (
-       "io"
-       "testing"
-)
-
-func FuzzEmpty(f *testing.F) {
-    f.Fuzz(func (*testing.T, []byte) {})
-}
-
-func FuzzY(f *testing.F) {
-       f.Add([]byte("y"))
-       f.Fuzz(func(t *testing.T, b []byte) { Y(io.Discard, b) })
-}
--- y.go --
-package y
-
-import (
-       "bytes"
-       "io"
-)
-
-func Y(w io.Writer, b []byte) {
-       if !bytes.Equal(b, []byte("y")) {
-               w.Write([]byte("not equal"))
-       }
-}
--- empty/empty.go --
-package empty
--- contains_files/contains_files.go --
-package main
-
-import (
-       "fmt"
-       "path/filepath"
-       "io/ioutil"
-       "os"
-)
-
-func main() {
-       infos, err := ioutil.ReadDir(filepath.Clean(os.Args[1]))
-       if err != nil {
-               fmt.Fprintln(os.Stderr, err)
-               os.Exit(1)
-       }
-       if len(infos) == 0 {
-               os.Exit(1)
-       }
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_cgo.txt b/src/cmd/go/testdata/script/test_fuzz_cgo.txt
deleted file mode 100644 (file)
index 1a04877..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-[!fuzz] skip
-[!cgo] skip
-[short] skip
-env GOCACHE=$WORK/cache
-
-# Test that fuzzing works with cgo (issue 65169)
-
-go test -fuzz=. -fuzztime=1x
-stdout ok
-! stdout FAIL
-
--- go.mod --
-module example.com/p
-
-go 1.20
--- c.go --
-package p
-
-import "C"
--- c_test.go --
-package p
-
-import "testing"
-
-func Fuzz(f *testing.F) {
-       f.Add(0)
-       f.Fuzz(func(t *testing.T, x int) {})
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_chatty.txt b/src/cmd/go/testdata/script/test_fuzz_chatty.txt
deleted file mode 100644 (file)
index 01a68cb..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-[!fuzz] skip
-[short] skip
-env GOCACHE=$WORK/cache
-
-# Run chatty fuzz targets with an error.
-! go test -v chatty_error_fuzz_test.go
-! stdout '^ok'
-stdout 'FAIL'
-stdout 'error in target'
-
-# Run chatty fuzz targets with a fatal.
-! go test -v chatty_fatal_fuzz_test.go
-! stdout '^ok'
-stdout 'FAIL'
-stdout 'fatal in target'
-
-# Run chatty fuzz target with a panic
-! go test -v chatty_panic_fuzz_test.go
-! stdout ^ok
-stdout FAIL
-stdout 'this is bad'
-
-# Run skipped chatty fuzz targets.
-go test -v chatty_skipped_fuzz_test.go
-stdout ok
-stdout SKIP
-! stdout FAIL
-
-# Run successful chatty fuzz targets.
-go test -v chatty_fuzz_test.go
-stdout ok
-stdout PASS
-stdout 'all good here'
-! stdout FAIL
-
-# Fuzz successful chatty fuzz target that includes a separate unit test.
-go test -v chatty_with_test_fuzz_test.go -fuzz=Fuzz -fuzztime=1x
-stdout ok
-stdout PASS
-! stdout FAIL
-stdout -count=1 'all good here'
-# Verify that the unit test is only run once.
-stdout -count=1 'logged foo'
-
--- chatty_error_fuzz_test.go --
-package chatty_error_fuzz
-
-import "testing"
-
-func Fuzz(f *testing.F) {
-    f.Error("error in target")
-}
-
--- chatty_fatal_fuzz_test.go --
-package chatty_fatal_fuzz
-
-import "testing"
-
-func Fuzz(f *testing.F) {
-    f.Fatal("fatal in target")
-}
-
--- chatty_panic_fuzz_test.go --
-package chatty_panic_fuzz
-
-import "testing"
-
-func Fuzz(f *testing.F) {
-    panic("this is bad")
-}
-
--- chatty_skipped_fuzz_test.go --
-package chatty_skipped_fuzz
-
-import "testing"
-
-func Fuzz(f *testing.F) {
-    f.Skip()
-}
-
--- chatty_fuzz_test.go --
-package chatty_fuzz
-
-import "testing"
-
-func Fuzz(f *testing.F) {
-    f.Log("all good here")
-    f.Fuzz(func(*testing.T, []byte) {})
-}
-
--- chatty_with_test_fuzz_test.go --
-package chatty_with_test_fuzz
-
-import "testing"
-
-func TestFoo(t *testing.T) {
-    t.Log("logged foo")
-}
-
-func Fuzz(f *testing.F) {
-    f.Log("all good here")
-    f.Fuzz(func(*testing.T, []byte) {})
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_cleanup.txt b/src/cmd/go/testdata/script/test_fuzz_cleanup.txt
deleted file mode 100644 (file)
index 5f86498..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-[!fuzz] skip
-[short] skip
-env GOCACHE=$WORK/cache
-
-# Cleanup should run after F.Skip.
-go test -run=FuzzTargetSkip
-stdout cleanup
-
-# Cleanup should run after F.Fatal.
-! go test -run=FuzzTargetFatal
-stdout cleanup
-
-# Cleanup should run after an unexpected runtime.Goexit.
-! go test -run=FuzzTargetGoexit
-stdout cleanup
-
-# Cleanup should run after panic.
-! go test -run=FuzzTargetPanic
-stdout cleanup
-
-# Cleanup should run in fuzz function on seed corpus.
-go test -v -run=FuzzFunction
-stdout '(?s)inner.*outer'
-
-# TODO(jayconrod): test cleanup while fuzzing. For now, the worker process's
-# stdout and stderr is connected to the coordinator's, but it should eventually
-# be connected to os.DevNull, so we wouldn't see t.Log output.
-
--- go.mod --
-module cleanup
-
-go 1.15
--- cleanup_test.go --
-package cleanup
-
-import (
-       "runtime"
-       "testing"
-)
-
-func FuzzTargetSkip(f *testing.F) {
-       f.Cleanup(func() { f.Log("cleanup") })
-       f.Skip()
-}
-
-func FuzzTargetFatal(f *testing.F) {
-       f.Cleanup(func() { f.Log("cleanup") })
-       f.Fatal()
-}
-
-func FuzzTargetGoexit(f *testing.F) {
-       f.Cleanup(func() { f.Log("cleanup") })
-       runtime.Goexit()
-}
-
-func FuzzTargetPanic(f *testing.F) {
-       f.Cleanup(func() { f.Log("cleanup") })
-       panic("oh no")
-}
-
-func FuzzFunction(f *testing.F) {
-       f.Add([]byte{0})
-       f.Cleanup(func() { f.Log("outer") })
-       f.Fuzz(func(t *testing.T, b []byte) {
-               t.Cleanup(func() { t.Logf("inner") })
-       })
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_context.txt b/src/cmd/go/testdata/script/test_fuzz_context.txt
deleted file mode 100644 (file)
index a830684..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-[!fuzz] skip
-[short] skip
-env GOCACHE=$WORK/cache
-
-# Test fuzz.Context.
-go test -vet=off context_fuzz_test.go
-stdout ^ok
-! stdout FAIL
-
-go test -vet=off -fuzz=Fuzz -fuzztime=1x context_fuzz_test.go
-stdout ok
-! stdout FAIL
-
--- context_fuzz_test.go --
-package context_fuzz
-
-import (
-       "context"
-       "errors"
-       "testing"
-)
-
-func Fuzz(f *testing.F) {
-       ctx := f.Context()
-       if err := ctx.Err(); err != nil {
-               f.Fatalf("expected non-canceled context, got %v", err)
-       }
-
-       f.Fuzz(func(t *testing.T, data []byte) {
-               innerCtx := t.Context()
-               if err := innerCtx.Err(); err != nil {
-                       t.Fatalf("expected inner test to not inherit canceled context, got %v", err)
-               }
-
-               t.Cleanup(func() {
-                       if !errors.Is(innerCtx.Err(), context.Canceled) {
-                               t.Fatal("expected context of inner test to be canceled after its fuzz function finished")
-                       }
-               })
-       })
-
-       f.Cleanup(func() {
-               if !errors.Is(ctx.Err(), context.Canceled) {
-                       f.Fatal("expected context canceled before cleanup")
-               }
-       })
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_cov.txt b/src/cmd/go/testdata/script/test_fuzz_cov.txt
deleted file mode 100644 (file)
index c0844a3..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-# Test that coverage instrumentation is working. Without the instrumentation
-# it is _extremely_ unlikely that the fuzzer would produce this particular
-# input in any reasonable amount of time.
-
-[short] skip
-[!fuzz-instrumented] skip
-env GOCACHE=$WORK/cache
-
-# TODO(#51484): enabled debugging info to help diagnose a deadlock in the fuzzer
-env GODEBUG=fuzzdebug=1
-! go test -fuzz=FuzzCov -v
-! stderr 'cov instrumentation working'
-
--- go.mod --
-module test
-
--- cov_test.go --
-package cov
-
-import "testing"
-
-func FuzzCov(f *testing.F) {
-       f.Fuzz(func(t *testing.T, b []byte) {
-               if len(b) == 8 &&
-                       b[0] == 'h' &&
-                       b[1] == 'e' &&
-                       b[2] == 'l' &&
-                       b[3] == 'l' &&
-                       b[4] == 'o' &&
-                       b[5] == ' ' &&
-                       b[6] == ':' &&
-                       b[7] == ')' {
-                       panic("cov instrumentation working")
-               }
-       })
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_deadline.txt b/src/cmd/go/testdata/script/test_fuzz_deadline.txt
deleted file mode 100644 (file)
index a51df34..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-[!fuzz] skip
-[short] skip
-env GOCACHE=$WORK/cache
-
-# Warm up the build cache with GOMAXPROCS unrestricted.
-go test -c -o $devnull
-
-# For the fuzzing phase, we reduce GOMAXPROCS to avoid consuming too many
-# resources during the test. Ideally this would just free up resources to run
-# other parallel tests more quickly, but unfortunately it is actually necessary
-# in some 32-bit environments to prevent the fuzzing engine from running out of
-# address space (see https://go.dev/issue/65434).
-env GOMAXPROCS=2
-
-# The fuzz function should be able to detect whether -timeout
-# was set with T.Deadline. Note there is no F.Deadline, and
-# there is no timeout while fuzzing, even if -fuzztime is set.
-go test -run=FuzzDeadline -wantdeadline=true # -timeout defaults to 10m
-go test -run=FuzzDeadline -timeout=0 -wantdeadline=false
-! go test -run=FuzzDeadline -timeout=1s -wantdeadline=false
-go test -run=FuzzDeadline -timeout=1s -wantdeadline=true
-go test -fuzz=FuzzDeadline -timeout=0 -fuzztime=1s -wantdeadline=false
-go test -fuzz=FuzzDeadline -timeout=0 -fuzztime=100x -wantdeadline=false
-
--- go.mod --
-module fuzz
-
-go 1.16
--- fuzz_deadline_test.go --
-package fuzz_test
-
-import (
-       "flag"
-       "testing"
-)
-
-var wantDeadline = flag.Bool("wantdeadline", false, "whether the test should have a deadline")
-
-func FuzzDeadline(f *testing.F) {
-       f.Add("run once")
-       f.Fuzz(func (t *testing.T, _ string) {
-               if _, hasDeadline := t.Deadline(); hasDeadline != *wantDeadline {
-                       t.Fatalf("function got %v; want %v", hasDeadline, *wantDeadline)
-               }
-       })
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_dup_cache.txt b/src/cmd/go/testdata/script/test_fuzz_dup_cache.txt
deleted file mode 100644 (file)
index f54a77c..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-[!fuzz] skip
-[short] skip
-env GOCACHE=$WORK/cache
-
-# This test checks that cached corpus loading properly handles duplicate entries (this can
-# happen when a f.Add value has a duplicate entry in the cached corpus.) Duplicate entries
-# should be discarded, and the rest of the cache should be loaded as normal.
-
-env GOCACHE=$WORK/cache
-env GODEBUG=fuzzdebug=1
-
-mkdir -p $GOCACHE/fuzz/fuzztest/FuzzTarget
-go run ./populate $GOCACHE/fuzz/fuzztest/FuzzTarget
-
-go test -fuzz=FuzzTarget -fuzztime=10x .
-stdout 'entries: 5'
-
--- go.mod --
-module fuzztest
-
-go 1.17
-
--- fuzz_test.go --
-package fuzz
-
-import "testing"
-
-func FuzzTarget(f *testing.F) {
-    f.Add(int(0))
-    f.Fuzz(func(t *testing.T, _ int) {})
-}
-
--- populate/main.go --
-package main
-
-import (
-    "path/filepath"
-       "fmt"
-       "os"
-)
-
-func main() {
-       for i := 0; i < 10; i++ {
-               b := byte(0)
-               if i > 5 {
-                       b = byte(i)
-               }
-        tmpl := "go test fuzz v1\nint(%d)\n"
-               if err := os.WriteFile(filepath.Join(os.Args[1], fmt.Sprint(i)), []byte(fmt.Sprintf(tmpl, b)), 0777); err != nil {
-                       panic(err)
-               }
-       }
-}
\ No newline at end of file
diff --git a/src/cmd/go/testdata/script/test_fuzz_err_deadlock.txt b/src/cmd/go/testdata/script/test_fuzz_err_deadlock.txt
deleted file mode 100644 (file)
index 4feb41a..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-[short] skip
-[!fuzz-instrumented] skip
-
-env GOCACHE=$WORK/cache
-! go test -fuzz=FuzzDead -v
-# This is a somewhat inexact check, but since we don't prefix the error with anything
-# and as the error suffix is platform dependent, this is the best we can do. In the
-# deadlock failure case, the test will just deadlock and timeout anyway, so it should
-# be clear that that failure mode is different.
-stdout 'open'
-
--- go.mod --
-module test
-
--- cov_test.go --
-package dead
-
-import (
-       "os"
-       "path/filepath"
-       "testing"
-       "time"
-)
-
-func FuzzDead(f *testing.F) {
-       go func() {
-               c := filepath.Join(os.Getenv("GOCACHE"), "fuzz", "test", "FuzzDead")
-               t := time.NewTicker(time.Second)
-               for range t.C {
-                       files, _ := os.ReadDir(c)
-                       if len(files) > 0 {
-                               os.RemoveAll(c)
-                       }
-               }
-       }()
-
-       f.Fuzz(func(t *testing.T, b []byte) {
-               if len(b) == 8 &&
-                       b[0] == 'h' &&
-                       b[1] == 'e' &&
-                       b[2] == 'l' &&
-                       b[3] == 'l' &&
-                       b[4] == 'o' &&
-                       b[5] == ' ' &&
-                       b[6] == ':' &&
-                       b[7] == ')' {
-                       return
-               }
-       })
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_fuzztime.txt b/src/cmd/go/testdata/script/test_fuzz_fuzztime.txt
deleted file mode 100644 (file)
index 3cc2398..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-skip # a 5s timeout is never going to be reliable (go.dev/issue/72140)
-
-[!fuzz] skip
-[short] skip
-env GOCACHE=$WORK/cache
-
-# There are no seed values, so 'go test' should finish quickly.
-go test
-
-# For the fuzzing phase, we reduce GOMAXPROCS to avoid consuming too many
-# resources during the test. Ideally this would just free up resources to run
-# other parallel tests more quickly, but unfortunately it is actually necessary
-# in some 32-bit environments to prevent the fuzzing engine from running out of
-# address space (see https://go.dev/issue/65434).
-env GOMAXPROCS=2
-
-# Fuzzing should exit 0 after fuzztime, even if timeout is short.
-go test -timeout=3s -fuzz=FuzzFast -fuzztime=5s
-
-# We should see the same behavior when invoking the test binary directly.
-go test -c
-exec ./fuzz.test$GOEXE -test.timeout=3s -test.fuzz=FuzzFast -test.fuzztime=5s -test.parallel=1 -test.fuzzcachedir=$WORK/cache
-
-# Timeout should not cause inputs to be written as crashers.
-! exists testdata/fuzz
-
-# When we use fuzztime with an "x" suffix, it runs a specific number of times.
-# This fuzz function creates a file with a unique name ($pid.$count) on each
-# run. We count the files to find the number of runs.
-mkdir count
-go test -fuzz=FuzzTestCount -fuzztime=1000x -fuzzminimizetime=1x
-go run check_file_count.go count 1000
-
-# When we use fuzzminimizetime with an "x" suffix, it runs a specific number of
-# times while minimizing. This fuzz function creates a file with a unique name
-# ($pid.$count) on each run once the first crash has been found. That means that
-# there should be one file for each execution of the fuzz function during
-# minimization, so we count these to determine how many times minimization was
-# run.
-mkdir minimizecount
-! go test -fuzz=FuzzMinimizeCount -fuzzminimizetime=3x -parallel=1
-go run check_file_count.go minimizecount 3
-
--- go.mod --
-module fuzz
-
-go 1.16
--- fuzz_fast_test.go --
-package fuzz_test
-
-import "testing"
-
-func FuzzFast(f *testing.F) {
-       f.Fuzz(func (*testing.T, []byte) {})
-}
--- fuzz_count_test.go --
-package fuzz
-
-import (
-       "fmt"
-       "os"
-       "testing"
-)
-
-func FuzzTestCount(f *testing.F) {
-       pid := os.Getpid()
-       n := 0
-       f.Fuzz(func(t *testing.T, _ []byte) {
-               name := fmt.Sprintf("count/%v.%d", pid, n)
-               if err := os.WriteFile(name, nil, 0666); err != nil {
-                       t.Fatal(err)
-               }
-               n++
-       })
-}
--- fuzz_minimize_count_test.go --
-package fuzz
-
-import (
-       "bytes"
-       "fmt"
-       "os"
-       "testing"
-)
-
-func FuzzMinimizeCount(f *testing.F) {
-       pid := os.Getpid()
-       n := 0
-       seed := bytes.Repeat([]byte("a"), 357)
-       f.Add(seed)
-       crashFound := false
-       f.Fuzz(func(t *testing.T, b []byte) {
-               if crashFound {
-                       name := fmt.Sprintf("minimizecount/%v.%d", pid, n)
-                       if err := os.WriteFile(name, nil, 0666); err != nil {
-                               t.Fatal(err)
-                       }
-                       n++
-               }
-               if !bytes.Equal(b, seed) {  // this should happen right away
-                       crashFound = true
-                       t.Error("minimize this!")
-               }
-       })
-}
--- check_file_count.go --
-// +build ignore
-
-package main
-
-import (
-       "fmt"
-       "os"
-       "strconv"
-)
-
-func main() {
-       dir, err := os.ReadDir(os.Args[1])
-       if err != nil {
-               fmt.Fprintln(os.Stderr, err)
-               os.Exit(1)
-       }
-       got := len(dir)
-       want, _ := strconv.Atoi(os.Args[2])
-       if got != want {
-               fmt.Fprintf(os.Stderr, "got %d files; want %d\n", got, want)
-               os.Exit(1)
-       }
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_io_error.txt b/src/cmd/go/testdata/script/test_fuzz_io_error.txt
deleted file mode 100644 (file)
index 01b4da6..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-# Test that when the coordinator experiences an I/O error communicating
-# with a worker, the coordinator stops the worker and reports the error.
-# The coordinator should not record a crasher.
-#
-# We simulate an I/O error in the test by writing garbage to fuzz_out.
-# This is unlikely, but possible. It's difficult to simulate interruptions
-# due to ^C and EOF errors which are more common. We don't report those.
-[short] skip
-[!fuzz] skip
-env GOCACHE=$WORK/cache
-
-# If the I/O error occurs before F.Fuzz is called, the coordinator should
-# stop the worker and say that.
-! go test -fuzz=FuzzClosePipeBefore -parallel=1
-stdout '\s*fuzzing process terminated without fuzzing:'
-! stdout 'communicating with fuzzing process'
-! exists testdata
-
-# If the I/O error occurs after F.Fuzz is called (unlikely), just exit.
-# It's hard to distinguish this case from the worker being interrupted by ^C
-# or exiting with status 0 (which it should do when interrupted by ^C).
-! go test -fuzz=FuzzClosePipeAfter -parallel=1
-stdout '^\s*communicating with fuzzing process: invalid character ''!'' looking for beginning of value$'
-! exists testdata
-
--- go.mod --
-module test
-
-go 1.17
--- io_error_test.go --
-package io_error
-
-import (
-       "flag"
-       "testing"
-       "time"
-)
-
-func isWorker() bool {
-       f := flag.Lookup("test.fuzzworker")
-       if f == nil {
-               return false
-       }
-       get, ok := f.Value.(flag.Getter)
-       if !ok {
-               return false
-       }
-       return get.Get() == interface{}(true)
-}
-
-func FuzzClosePipeBefore(f *testing.F) {
-       if isWorker() {
-               sendGarbageToCoordinator(f)
-               time.Sleep(3600 * time.Second) // pause until coordinator terminates the process
-       }
-       f.Fuzz(func(*testing.T, []byte) {})
-}
-
-func FuzzClosePipeAfter(f *testing.F) {
-       f.Fuzz(func(t *testing.T, _ []byte) {
-               if isWorker() {
-                       sendGarbageToCoordinator(t)
-                       time.Sleep(3600 * time.Second) // pause until coordinator terminates the process
-               }
-       })
-}
--- io_error_windows_test.go --
-package io_error
-
-import (
-       "fmt"
-       "os"
-       "testing"
-)
-
-func sendGarbageToCoordinator(tb testing.TB) {
-       v := os.Getenv("GO_TEST_FUZZ_WORKER_HANDLES")
-       var fuzzInFD, fuzzOutFD uintptr
-       if _, err := fmt.Sscanf(v, "%x,%x", &fuzzInFD, &fuzzOutFD); err != nil {
-               tb.Fatalf("parsing GO_TEST_FUZZ_WORKER_HANDLES: %v", err)
-       }
-       f := os.NewFile(fuzzOutFD, "fuzz_out")
-       if _, err := f.Write([]byte("!!")); err != nil {
-               tb.Fatalf("writing fuzz_out: %v", err)
-       }
-}
--- io_error_notwindows_test.go --
-// +build !windows
-
-package io_error
-
-import (
-       "os"
-       "testing"
-)
-
-func sendGarbageToCoordinator(tb testing.TB) {
-       f := os.NewFile(4, "fuzz_out")
-       if _, err := f.Write([]byte("!!")); err != nil {
-               tb.Fatalf("writing fuzz_out: %v", err)
-       }
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_limit_dup_entry.txt b/src/cmd/go/testdata/script/test_fuzz_limit_dup_entry.txt
deleted file mode 100644 (file)
index d69f6e0..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-[!fuzz] skip
-[short] skip
-env GOCACHE=$WORK/cache
-
-# FuzzA attempts to cause the mutator to create duplicate inputs that generate
-# new coverage. Previously this would trigger a corner case when the fuzzer
-# had an execution limit, causing it to deadlock and sit in the coordinator
-# loop indefinitely, failing to exit once the limit had been exhausted.
-
-go test -fuzz=FuzzA -fuzztime=100x -parallel=1
-
--- go.mod --
-module m
-
-go 1.16
--- fuzz_test.go --
-package fuzz_test
-
-import (
-       "fmt"
-       "testing"
-)
-
-func FuzzA(f *testing.F) {
-       f.Add([]byte("seed"))
-       i := 0
-       f.Fuzz(func(t *testing.T, b []byte) {
-               i++
-               if string(b) == "seed" {
-                       if i == 0 {
-                               fmt.Println("a")
-                       } else if i > 1 {
-                               fmt.Println("b")
-                       }
-               }
-       })
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_match.txt b/src/cmd/go/testdata/script/test_fuzz_match.txt
deleted file mode 100644 (file)
index d149586..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-[!fuzz] skip
-[short] skip
-env GOCACHE=$WORK/cache
-
-# Matches only fuzz targets to test.
-go test standalone_fuzz_test.go
-! stdout '^ok.*\[no tests to run\]'
-stdout '^ok'
-
-# Matches only for fuzzing.
-go test -fuzz Fuzz -fuzztime 1x standalone_fuzz_test.go
-! stdout '^ok.*\[no tests to run\]'
-stdout '^ok'
-
-# Matches none for fuzzing but will run the fuzz target as a test.
-go test -fuzz ThisWillNotMatch -fuzztime 1x standalone_fuzz_test.go
-! stdout '^ok.*no tests to run'
-stdout '^ok'
-stdout 'no fuzz tests to fuzz'
-
-[short] stop
-
-# Matches only fuzz targets to test with -run.
-go test -run Fuzz standalone_fuzz_test.go
-! stdout '^ok.*\[no tests to run\]'
-stdout '^ok'
-
-# Matches no fuzz targets.
-go test -run ThisWillNotMatch standalone_fuzz_test.go
-stdout '^ok.*no tests to run'
-! stdout 'no fuzz tests to fuzz'
-
--- standalone_fuzz_test.go --
-package standalone_fuzz
-
-import "testing"
-
-func Fuzz(f *testing.F) {
-       f.Fuzz(func (*testing.T, []byte) {})
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_minimize.txt b/src/cmd/go/testdata/script/test_fuzz_minimize.txt
deleted file mode 100644 (file)
index a6dc3f1..0000000
+++ /dev/null
@@ -1,203 +0,0 @@
-[!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)
-       }
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_minimize_dirty_cov.txt b/src/cmd/go/testdata/script/test_fuzz_minimize_dirty_cov.txt
deleted file mode 100644 (file)
index c8af9be..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-# Test that minimization doesn't use dirty coverage snapshots when it
-# is unable to actually minimize the input. We do this by checking that
-# an expected value appears in the cache. If a dirty coverage map is used
-# (i.e. the coverage map generated during the last minimization step,
-# rather than the map provided with the initial input) then this value
-# is unlikely to appear in the cache, since the map generated during
-# the last minimization step should not increase the coverage.
-
-[short] skip
-[!fuzz-instrumented] skip
-
-env GOCACHE=$WORK/gocache
-go test -fuzz=FuzzCovMin -fuzztime=500000x -test.fuzzcachedir=$GOCACHE/fuzz
-go run check_file/main.go $GOCACHE/fuzz/FuzzCovMin ab
-
--- go.mod --
-module test
-
--- covmin_test.go --
-package covmin
-
-import "testing"
-
-func FuzzCovMin(f *testing.F) {
-       f.Add([]byte("aa"))
-       f.Fuzz(func(t *testing.T, data []byte) {
-               if len(data) == 2 && data[0] == 'a' && data[1] == 'b' {
-                       return
-               }
-       })
-}
-
--- check_file/main.go --
-package main
-
-import (
-       "bytes"
-       "fmt"
-       "os"
-       "path/filepath"
-       "regexp"
-       "strconv"
-)
-
-func checkFile(name, expected string) (bool, error) {
-       data, err := os.ReadFile(name)
-       if err != nil {
-               return false, err
-       }
-       for _, line := range bytes.Split(data, []byte("\n")) {
-               m := valRe.FindSubmatch(line)
-               if m == nil {
-                       continue
-               }
-               fmt.Println(strconv.Unquote(string(m[1])))
-               if s, err := strconv.Unquote(string(m[1])); err != nil {
-                       return false, err
-               } else if s == expected {
-                       return true, nil
-               }
-       }
-       return false, nil
-}
-
-var valRe = regexp.MustCompile(`^\[\]byte\(([^)]+)\)$`)
-
-func main() {
-       dir, expected := os.Args[1], os.Args[2]
-       ents, err := os.ReadDir(dir)
-       if err != nil {
-               fmt.Fprintln(os.Stderr, err)
-               os.Exit(1)
-       }
-       for _, ent := range ents {
-               name := filepath.Join(dir, ent.Name())
-               if good, err := checkFile(name, expected); err != nil {
-                       fmt.Fprintln(os.Stderr, err)
-                       os.Exit(1)
-               } else if good {
-                       os.Exit(0)
-               }
-       }
-       fmt.Fprintln(os.Stderr, "input over minimized")
-       os.Exit(1)
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt b/src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt
deleted file mode 100644 (file)
index 11aaaca..0000000
+++ /dev/null
@@ -1,230 +0,0 @@
-[short] skip
-[!fuzz-instrumented] skip
-
-# Test that when an interesting value is discovered (one that expands coverage),
-# the fuzzing engine minimizes it before writing it to the cache.
-#
-# The program below starts with a seed value of length 100, but more coverage
-# will be found for any value other than the seed. We should end with a value
-# in the cache of length 1 (the minimizer currently does not produce empty
-# strings). check_cache.go confirms that.
-#
-# We would like to verify that ALL values in the cache were minimized to a
-# length of 1, but this isn't always possible when new coverage is found in
-# functions called by testing or internal/fuzz in the background.
-
-go test -c -fuzz=.  # Build using shared build cache for speed.
-env GOCACHE=$WORK/gocache
-exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinCache -test.fuzztime=1000x
-go run check_cache/check_cache.go $GOCACHE/fuzz/FuzzMinCache
-
-# Test that minimization occurs for a crash that appears while minimizing a
-# newly found interesting input. There must be only one worker for this test to
-# be flaky like we want.
-! exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinimizerCrashInMinimization -test.run=^$ -test.fuzztime=10000x -test.parallel=1
-! stdout '^ok'
-stdout -count=1 'got the minimum size!'
-stdout -count=1 'bad input'
-stdout FAIL
-# Check that the input written to testdata will reproduce the error, and is the
-# smallest possible.
-go run check_testdata/check_testdata.go FuzzMinimizerCrashInMinimization 1
-
-# Test that a nonrecoverable error that occurs while minimizing an interesting
-# input is reported correctly.
-! exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinimizerNonrecoverableCrashInMinimization -test.run=^$ -test.fuzztime=10000x -test.parallel=1
-! stdout '^ok'
-stdout -count=1 'fuzzing process hung or terminated unexpectedly while minimizing'
-stdout -count=1 'EOF'
-stdout FAIL
-# Check that the input written to testdata will reproduce the error.
-go run check_testdata/check_testdata.go FuzzMinimizerNonrecoverableCrashInMinimization 1
-
--- go.mod --
-module fuzz
-
-go 1.17
--- y.go --
-package fuzz
-
-import (
-       "bytes"
-       "io"
-)
-
-func Y(w io.Writer, s string) {
-       if !bytes.Equal([]byte(s), []byte("y")) {
-               w.Write([]byte("not equal"))
-       }
-}
--- fuzz_test.go --
-package fuzz
-
-import (
-       "bytes"
-       "os"
-       "testing"
-)
-
-func FuzzMinimizerCrashInMinimization(f *testing.F) {
-       seed := bytes.Repeat([]byte{255}, 100)
-       f.Add(seed)
-       f.Fuzz(func(t *testing.T, b []byte) {
-               if bytes.Equal(seed, b) {
-                       return
-               }
-               t.Error("bad input")
-               if len(b) == 1 {
-                       t.Error("got the minimum size!")
-               }
-       })
-}
-
-var fuzzing bool
-
-func FuzzMinimizerNonrecoverableCrashInMinimization(f *testing.F) {
-       seed := bytes.Repeat([]byte{255}, 100)
-       f.Add(seed)
-       f.Fuzz(func(t *testing.T, b []byte) {
-               if bytes.Equal(seed, b) {
-                       return
-               } else if len(b) == 1 {
-                       os.Exit(1)
-               }
-       })
-}
-
-func FuzzMinCache(f *testing.F) {
-       seed := bytes.Repeat([]byte("a"), 20)
-       f.Add(seed)
-       f.Fuzz(func(t *testing.T, buf []byte) {
-               if bytes.Equal(buf, seed) {
-                       return
-               }
-       })
-}
--- check_testdata/check_testdata.go --
-//go:build ignore
-// +build ignore
-
-// check_testdata.go checks that the string written
-// is not longer than the provided length.
-package main
-
-import (
-       "bytes"
-       "fmt"
-       "io/ioutil"
-       "os"
-       "path/filepath"
-       "regexp"
-       "strconv"
-)
-
-func main() {
-       wantLen, err := strconv.Atoi(os.Args[2])
-       if err != nil {
-               fmt.Fprintln(os.Stderr, err)
-               os.Exit(1)
-       }
-       testName := os.Args[1]
-       dir := filepath.Join("testdata/fuzz", testName)
-
-       files, err := ioutil.ReadDir(dir)
-       if err != nil {
-               fmt.Fprintln(os.Stderr, err)
-               os.Exit(1)
-       }
-
-       if len(files) == 0 {
-               fmt.Fprintf(os.Stderr, "expect at least one failure to be written to testdata\n")
-               os.Exit(1)
-       }
-
-       for _, f := range files {
-               data, err := ioutil.ReadFile(filepath.Join(dir, f.Name()))
-               if err != nil {
-                       panic(err)
-               }
-               var containsVal bool
-               for _, line := range bytes.Split(data, []byte("\n")) {
-                       m := valRe.FindSubmatch(line)
-                       if m == nil {
-                               continue
-                       }
-                       containsVal = true
-                       s, err := strconv.Unquote(string(m[1]))
-                       if err != nil {
-                               panic(err)
-                       }
-                       if len(s) != wantLen {
-                               fmt.Fprintf(os.Stderr, "expect length %d, got %d (%q)\n", wantLen, len(s), line)
-                               os.Exit(1)
-                       }
-               }
-               if !containsVal {
-                       fmt.Fprintln(os.Stderr, "corpus file contained no values")
-                       os.Exit(1)
-               }
-       }
-}
-
-var valRe = regexp.MustCompile(`^\[\]byte\(([^)]+)\)$`)
-
--- check_cache/check_cache.go --
-//go:build ignore
-// +build ignore
-
-// check_cache.go checks that each file in the cached corpus has a []byte
-// of length at most 1. This verifies that at least one cached input is minimized.
-package main
-
-import (
-       "bytes"
-       "fmt"
-       "os"
-       "path/filepath"
-       "regexp"
-       "strconv"
-)
-
-func main() {
-       dir := os.Args[1]
-       ents, err := os.ReadDir(dir)
-       if err != nil {
-               fmt.Fprintln(os.Stderr, err)
-               os.Exit(1)
-       }
-       for _, ent := range ents {
-               name := filepath.Join(dir, ent.Name())
-               if good, err := checkCacheFile(name); err != nil {
-                       fmt.Fprintln(os.Stderr, err)
-                       os.Exit(1)
-               } else if good {
-                       os.Exit(0)
-               }
-       }
-       fmt.Fprintln(os.Stderr, "no cached inputs were minimized")
-       os.Exit(1)
-}
-
-func checkCacheFile(name string) (good bool, err error) {
-       data, err := os.ReadFile(name)
-       if err != nil {
-               return false, err
-       }
-       for _, line := range bytes.Split(data, []byte("\n")) {
-               m := valRe.FindSubmatch(line)
-               if m == nil {
-                       continue
-               }
-               if s, err := strconv.Unquote(string(m[1])); err != nil {
-                       return false, err
-               } else if len(s) <= 1 {
-                       return true, nil
-               }
-       }
-       return false, nil
-}
-
-var valRe = regexp.MustCompile(`^\[\]byte\(([^)]+)\)$`)
diff --git a/src/cmd/go/testdata/script/test_fuzz_multiple.txt b/src/cmd/go/testdata/script/test_fuzz_multiple.txt
deleted file mode 100644 (file)
index c96112f..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-# This test checks that 'go test' prints a reasonable error when fuzzing is
-# enabled, and multiple package or multiple fuzz targets match.
-# TODO(#46312): support fuzzing multiple targets in multiple packages.
-
-[!fuzz] skip
-[short] skip
-env GOCACHE=$WORK/cache
-
-# With fuzzing disabled, multiple targets can be tested.
-go test ./...
-
-# With fuzzing enabled, at most one package may be tested,
-# even if only one package contains fuzz targets.
-! go test -fuzz=. ./...
-stderr '^cannot use -fuzz flag with multiple packages$'
-! go test -fuzz=. ./zero ./one
-stderr '^cannot use -fuzz flag with multiple packages$'
-go test -fuzz=. -fuzztime=1x ./one
-
-# With fuzzing enabled, at most one target in the same package may match.
-! go test -fuzz=. ./two
-stdout '^testing: will not fuzz, -fuzz matches more than one fuzz test: \[FuzzOne FuzzTwo\]$'
-go test -fuzz=FuzzTwo -fuzztime=1x ./two
-
--- go.mod --
-module fuzz
-
-go 1.18
--- zero/zero.go --
-package zero
--- one/one_test.go --
-package one
-
-import "testing"
-
-func FuzzOne(f *testing.F) {
-  f.Fuzz(func(*testing.T, []byte) {})
-}
--- two/two_test.go --
-package two
-
-import "testing"
-
-func FuzzOne(f *testing.F) {
-  f.Fuzz(func(*testing.T, []byte) {})
-}
-
-func FuzzTwo(f *testing.F) {
-  f.Fuzz(func(*testing.T, []byte) {})
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_mutate_crash.txt b/src/cmd/go/testdata/script/test_fuzz_mutate_crash.txt
deleted file mode 100644 (file)
index 4b9b36d..0000000
+++ /dev/null
@@ -1,323 +0,0 @@
-[!fuzz] skip
-
-# Tests that a crash caused by a mutator-discovered input writes the bad input
-# to testdata, and fails+reports correctly. This tests the end-to-end behavior
-# of the mutator finding a crash while fuzzing, adding it as a regression test
-# to the seed corpus in testdata, and failing the next time the test is run.
-
-[short] skip
-env GOCACHE=$WORK/cache
-
-# Running the seed corpus for all of the targets should pass the first
-# time, since nothing in the seed corpus will cause a crash.
-go test
-
-# Running the fuzzer should find a crashing input quickly.
-! go test -fuzz=FuzzWithBug -fuzztime=100x -fuzzminimizetime=1000x
-stdout 'testdata[/\\]fuzz[/\\]FuzzWithBug[/\\]'
-stdout 'this input caused a crash!'
-go run check_testdata.go FuzzWithBug
-
-# Now, the failing bytes should have been added to the seed corpus for
-# the target, and should fail when run without fuzzing.
-! go test
-stdout 'FuzzWithBug/[a-f0-9]{16}'
-stdout 'this input caused a crash!'
-
-! go test -run=FuzzWithNilPanic -fuzz=FuzzWithNilPanic -fuzztime=100x -fuzzminimizetime=1000x
-stdout 'testdata[/\\]fuzz[/\\]FuzzWithNilPanic[/\\]'
-stdout 'panic called with nil argument|test executed panic.nil. or runtime.Goexit'
-go run check_testdata.go FuzzWithNilPanic
-
-! go test -run=FuzzWithGoexit -fuzz=FuzzWithGoexit -fuzztime=100x -fuzzminimizetime=1000x
-stdout 'testdata[/\\]fuzz[/\\]FuzzWithGoexit[/\\]'
-stdout 'runtime.Goexit'
-go run check_testdata.go FuzzWithGoexit
-
-! go test -run=FuzzWithFail -fuzz=FuzzWithFail -fuzztime=100x -fuzzminimizetime=1000x
-stdout 'testdata[/\\]fuzz[/\\]FuzzWithFail[/\\]'
-go run check_testdata.go FuzzWithFail
-
-! go test -run=FuzzWithLogFail -fuzz=FuzzWithLogFail -fuzztime=100x -fuzzminimizetime=1000x
-stdout 'testdata[/\\]fuzz[/\\]FuzzWithLogFail[/\\]'
-stdout 'logged something'
-go run check_testdata.go FuzzWithLogFail
-
-! go test -run=FuzzWithErrorf -fuzz=FuzzWithErrorf -fuzztime=100x -fuzzminimizetime=1000x
-stdout 'testdata[/\\]fuzz[/\\]FuzzWithErrorf[/\\]'
-stdout 'errorf was called here'
-go run check_testdata.go FuzzWithErrorf
-
-! go test -run=FuzzWithFatalf -fuzz=FuzzWithFatalf -fuzztime=100x -fuzzminimizetime=1000x
-stdout 'testdata[/\\]fuzz[/\\]FuzzWithFatalf[/\\]'
-stdout 'fatalf was called here'
-go run check_testdata.go FuzzWithFatalf
-
-! go test -run=FuzzWithBadExit -fuzz=FuzzWithBadExit -fuzztime=100x -fuzzminimizetime=1000x
-stdout 'testdata[/\\]fuzz[/\\]FuzzWithBadExit[/\\]'
-stdout '^\s+fuzzing process hung or terminated unexpectedly: exit status'
-go run check_testdata.go FuzzWithBadExit
-
-! go test -run=FuzzDeadlock -fuzz=FuzzDeadlock -fuzztime=100x -fuzzminimizetime=0x
-stdout 'testdata[/\\]fuzz[/\\]FuzzDeadlock[/\\]'
-stdout '^\s+fuzzing process hung or terminated unexpectedly: exit status'
-go run check_testdata.go FuzzDeadlock
-
-# Running the fuzzer should find a crashing input quickly for fuzzing two types.
-! go test -run=FuzzWithTwoTypes -fuzz=FuzzWithTwoTypes -fuzztime=100x -fuzzminimizetime=1000x
-stdout 'testdata[/\\]fuzz[/\\]FuzzWithTwoTypes[/\\]'
-stdout 'these inputs caused a crash!'
-go run check_testdata.go FuzzWithTwoTypes
-
-# Running the fuzzer should find a crashing input quickly for an integer.
-! go test -run=FuzzInt -fuzz=FuzzInt -fuzztime=100x -fuzzminimizetime=1000x
-stdout 'testdata[/\\]fuzz[/\\]FuzzInt[/\\]'
-stdout 'this input caused a crash!'
-go run check_testdata.go FuzzInt
-
-! go test -run=FuzzUint -fuzz=FuzzUint -fuzztime=100x -fuzzminimizetime=1000x
-stdout 'testdata[/\\]fuzz[/\\]FuzzUint[/\\]'
-stdout 'this input caused a crash!'
-go run check_testdata.go FuzzUint
-
-# Running the fuzzer should find a crashing input quickly for a bool.
-! go test -run=FuzzBool -fuzz=FuzzBool -fuzztime=100x -fuzzminimizetime=1000x
-stdout 'testdata[/\\]fuzz[/\\]FuzzBool[/\\]'
-stdout 'this input caused a crash!'
-go run check_testdata.go FuzzBool
-
-# Running the fuzzer should find a crashing input quickly for a float.
-! go test -run=FuzzFloat -fuzz=FuzzFloat -fuzztime=100x -fuzzminimizetime=1000x
-stdout 'testdata[/\\]fuzz[/\\]FuzzFloat[/\\]'
-stdout 'this input caused a crash!'
-go run check_testdata.go FuzzFloat
-
-# Running the fuzzer should find a crashing input quickly for a byte.
-! go test -run=FuzzByte -fuzz=FuzzByte -fuzztime=100x -fuzzminimizetime=1000x
-stdout 'testdata[/\\]fuzz[/\\]FuzzByte[/\\]'
-stdout 'this input caused a crash!'
-go run check_testdata.go FuzzByte
-
-# Running the fuzzer should find a crashing input quickly for a rune.
-! go test -run=FuzzRune -fuzz=FuzzRune -fuzztime=100x -fuzzminimizetime=1000x
-stdout 'testdata[/\\]fuzz[/\\]FuzzRune[/\\]'
-stdout 'this input caused a crash!'
-go run check_testdata.go FuzzRune
-
-# Running the fuzzer should find a crashing input quickly for a string.
-! go test -run=FuzzString -fuzz=FuzzString -fuzztime=100x -fuzzminimizetime=1000x
-stdout 'testdata[/\\]fuzz[/\\]FuzzString[/\\]'
-stdout 'this input caused a crash!'
-go run check_testdata.go FuzzString
-
--- go.mod --
-module m
-
-go 1.16
--- fuzz_crash_test.go --
-package fuzz_crash
-
-import (
-       "os"
-       "runtime"
-       "testing"
-)
-
-func FuzzWithBug(f *testing.F) {
-       f.Add([]byte("aa"))
-       f.Fuzz(func(t *testing.T, b []byte) {
-               if string(b) != "aa" {
-                       panic("this input caused a crash!")
-               }
-       })
-}
-
-func FuzzWithNilPanic(f *testing.F) {
-       f.Add([]byte("aa"))
-       f.Fuzz(func(t *testing.T, b []byte) {
-               if string(b) != "aa" {
-                       panic(nil)
-               }
-       })
-}
-
-func FuzzWithGoexit(f *testing.F) {
-       f.Add([]byte("aa"))
-       f.Fuzz(func(t *testing.T, b []byte) {
-               if string(b) != "aa" {
-                       runtime.Goexit()
-               }
-       })
-}
-
-func FuzzWithFail(f *testing.F) {
-       f.Add([]byte("aa"))
-       f.Fuzz(func(t *testing.T, b []byte) {
-               if string(b) != "aa" {
-                       t.Fail()
-               }
-       })
-}
-
-func FuzzWithLogFail(f *testing.F) {
-       f.Add([]byte("aa"))
-       f.Fuzz(func(t *testing.T, b []byte) {
-               if string(b) != "aa" {
-                       t.Log("logged something")
-                       t.Fail()
-               }
-       })
-}
-
-func FuzzWithErrorf(f *testing.F) {
-       f.Add([]byte("aa"))
-       f.Fuzz(func(t *testing.T, b []byte) {
-               if string(b) != "aa" {
-                       t.Errorf("errorf was called here")
-               }
-       })
-}
-
-func FuzzWithFatalf(f *testing.F) {
-       f.Add([]byte("aa"))
-       f.Fuzz(func(t *testing.T, b []byte) {
-               if string(b) != "aa" {
-                       t.Fatalf("fatalf was called here")
-               }
-       })
-}
-
-func FuzzWithBadExit(f *testing.F) {
-       f.Add([]byte("aa"))
-       f.Fuzz(func(t *testing.T, b []byte) {
-               if string(b) != "aa" {
-                       os.Exit(1)
-               }
-       })
-}
-
-func FuzzDeadlock(f *testing.F) {
-       f.Add(int(0))
-       f.Fuzz(func(t *testing.T, n int) {
-               if n != 0 {
-                       select {}
-               }
-       })
-}
-
-func FuzzWithTwoTypes(f *testing.F) {
-       f.Fuzz(func(t *testing.T, a, b []byte) {
-               if len(a) > 0 && len(b) > 0 {
-                       panic("these inputs caused a crash!")
-               }
-       })
-}
-
-func FuzzInt(f *testing.F) {
-       f.Add(0)
-       f.Fuzz(func(t *testing.T, a int) {
-               if a != 0 {
-                       panic("this input caused a crash!")
-               }
-       })
-}
-
-func FuzzUint(f *testing.F) {
-       f.Add(uint(0))
-       f.Fuzz(func(t *testing.T, a uint) {
-               if a != 0 {
-                       panic("this input caused a crash!")
-               }
-       })
-}
-
-func FuzzBool(f *testing.F) {
-       f.Add(false)
-       f.Fuzz(func(t *testing.T, a bool) {
-               if a {
-                       panic("this input caused a crash!")
-               }
-       })
-}
-
-func FuzzFloat(f *testing.F) {
-       f.Fuzz(func(t *testing.T, a float64) {
-               if a != 0 {
-                       panic("this input caused a crash!")
-               }
-       })
-}
-
-func FuzzByte(f *testing.F) {
-       f.Add(byte(0))
-       f.Fuzz(func(t *testing.T, a byte) {
-               if a != 0 {
-                       panic("this input caused a crash!")
-               }
-       })
-}
-
-func FuzzRune(f *testing.F) {
-       f.Add(rune(0))
-       f.Fuzz(func(t *testing.T, a rune) {
-               if a != 0 {
-                       panic("this input caused a crash!")
-               }
-       })
-}
-
-func FuzzString(f *testing.F) {
-       f.Add("")
-       f.Fuzz(func(t *testing.T, a string) {
-               if a != "" {
-                       panic("this input caused a crash!")
-               }
-       })
-}
-
--- check_testdata.go --
-// +build ignore
-
-package main
-
-import (
-       "bytes"
-       "crypto/sha256"
-       "fmt"
-       "io/ioutil"
-       "os"
-       "path/filepath"
-)
-
-func main() {
-       target := os.Args[1]
-       dir := filepath.Join("testdata/fuzz", target)
-
-       files, err := ioutil.ReadDir(dir)
-       if err != nil {
-               fmt.Fprintln(os.Stderr, err)
-               os.Exit(1)
-       }
-
-       if len(files) == 0 {
-               fmt.Fprintf(os.Stderr, "expect at least one new mutation to be written to testdata\n")
-               os.Exit(1)
-       }
-
-       fname := files[0].Name()
-       contents, err := ioutil.ReadFile(filepath.Join(dir, fname))
-       if err != nil {
-               fmt.Fprintln(os.Stderr, err)
-               os.Exit(1)
-       }
-       if bytes.Equal(contents, []byte("aa")) {
-               fmt.Fprintf(os.Stderr, "newly written testdata entry was not mutated\n")
-               os.Exit(1)
-       }
-       // The hash of the bytes in the file should match the filename.
-       h := []byte(fmt.Sprintf("%x", sha256.Sum256(contents)))
-       if !bytes.HasPrefix(h, []byte(fname)) {
-               fmt.Fprintf(os.Stderr, "hash of bytes %q does not match filename %q\n", h, fname)
-               os.Exit(1)
-       }
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_mutate_fail.txt b/src/cmd/go/testdata/script/test_fuzz_mutate_fail.txt
deleted file mode 100644 (file)
index 213b73a..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-[!fuzz] skip
-
-# Check that if a worker does not call F.Fuzz or calls F.Fail first,
-# 'go test' exits non-zero and no crasher is recorded.
-
-[short] skip
-env GOCACHE=$WORK/cache
-
-! go test -fuzz=FuzzReturn
-! exists testdata
-
-! go test -fuzz=FuzzSkip
-! exists testdata
-
-! go test -fuzz=FuzzFail
-! exists testdata
-
-! go test -fuzz=FuzzPanic
-! exists testdata
-
-! go test -fuzz=FuzzNilPanic
-! exists testdata
-
-! go test -fuzz=FuzzGoexit
-! exists testdata
-
-! go test -fuzz=FuzzExit
-! exists testdata
-
--- go.mod --
-module m
-
-go 1.17
--- fuzz_fail_test.go --
-package fuzz_fail
-
-import (
-       "flag"
-       "os"
-       "runtime"
-       "testing"
-)
-
-func isWorker() bool {
-       f := flag.Lookup("test.fuzzworker")
-       if f == nil {
-               return false
-       }
-       get, ok := f.Value.(flag.Getter)
-       if !ok {
-               return false
-       }
-       return get.Get() == interface{}(true)
-}
-
-func FuzzReturn(f *testing.F) {
-       if isWorker() {
-               return
-       }
-       f.Fuzz(func(*testing.T, []byte) {})
-}
-
-func FuzzSkip(f *testing.F) {
-       if isWorker() {
-               f.Skip()
-       }
-       f.Fuzz(func(*testing.T, []byte) {})
-}
-
-func FuzzFail(f *testing.F) {
-       if isWorker() {
-               f.Fail()
-       }
-       f.Fuzz(func(*testing.T, []byte) {})
-}
-
-func FuzzPanic(f *testing.F) {
-       if isWorker() {
-               panic("nope")
-       }
-       f.Fuzz(func(*testing.T, []byte) {})
-}
-
-func FuzzNilPanic(f *testing.F) {
-       if isWorker() {
-               panic(nil)
-       }
-       f.Fuzz(func(*testing.T, []byte) {})
-}
-
-func FuzzGoexit(f *testing.F) {
-       if isWorker() {
-               runtime.Goexit()
-       }
-       f.Fuzz(func(*testing.T, []byte) {})
-}
-
-func FuzzExit(f *testing.F) {
-       if isWorker() {
-               os.Exit(99)
-       }
-       f.Fuzz(func(*testing.T, []byte) {})
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_mutator.txt b/src/cmd/go/testdata/script/test_fuzz_mutator.txt
deleted file mode 100644 (file)
index cc1f989..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-[!fuzz] skip
-
-# Test basic fuzzing mutator behavior.
-#
-# fuzz_test.go has two fuzz targets (FuzzA, FuzzB) which both add a seed value.
-# Each fuzz function writes the input to a log file. The coordinator and worker
-# use separate log files. check_logs.go verifies that the coordinator only
-# tests seed values and the worker tests mutated values on the fuzz target.
-
-[short] skip
-env GOCACHE=$WORK/cache
-
-go test -fuzz=FuzzA -fuzztime=100x -parallel=1 -log=fuzz
-go run check_logs.go fuzz fuzz.worker
-
-# TODO(b/181800488): remove -parallel=1, here and below. For now, when a
-# crash is found, all workers keep running, wasting resources and reducing
-# the number of executions available to the minimizer, increasing flakiness.
-
-# Test that the mutator is good enough to find several unique mutations.
-! go test -fuzz=FuzzMutator -parallel=1 -fuzztime=100x mutator_test.go
-! stdout '^ok'
-stdout FAIL
-stdout 'mutator found enough unique mutations'
-
--- go.mod --
-module m
-
-go 1.16
--- fuzz_test.go --
-package fuzz_test
-
-import (
-       "flag"
-       "fmt"
-       "os"
-       "testing"
-)
-
-var (
-       logPath = flag.String("log", "", "path to log file")
-       logFile *os.File
-)
-
-func TestMain(m *testing.M) {
-       flag.Parse()
-       var err error
-       logFile, err = os.OpenFile(*logPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
-       if os.IsExist(err) {
-               *logPath += ".worker"
-               logFile, err = os.OpenFile(*logPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
-       }
-       if err != nil {
-               fmt.Fprintln(os.Stderr, err)
-               os.Exit(1)
-       }
-       os.Exit(m.Run())
-}
-
-func FuzzA(f *testing.F) {
-       f.Add([]byte("seed"))
-       f.Fuzz(func(t *testing.T, b []byte) {
-               fmt.Fprintf(logFile, "FuzzA %q\n", b)
-       })
-}
-
-func FuzzB(f *testing.F) {
-       f.Add([]byte("seed"))
-       f.Fuzz(func(t *testing.T, b []byte) {
-               fmt.Fprintf(logFile, "FuzzB %q\n", b)
-       })
-}
-
--- check_logs.go --
-// +build ignore
-
-package main
-
-import (
-       "bufio"
-       "bytes"
-       "fmt"
-       "io"
-       "os"
-       "strings"
-)
-
-func main() {
-       coordPath, workerPath := os.Args[1], os.Args[2]
-
-       coordLog, err := os.Open(coordPath)
-       if err != nil {
-               fmt.Fprintln(os.Stderr, err)
-               os.Exit(1)
-       }
-       defer coordLog.Close()
-       if err := checkCoordLog(coordLog); err != nil {
-               fmt.Fprintln(os.Stderr, err)
-               os.Exit(1)
-       }
-
-       workerLog, err := os.Open(workerPath)
-       if err != nil {
-               fmt.Fprintln(os.Stderr, err)
-               os.Exit(1)
-       }
-       defer workerLog.Close()
-       if err := checkWorkerLog(workerLog); err != nil {
-               fmt.Fprintln(os.Stderr, err)
-               os.Exit(1)
-       }
-}
-
-func checkCoordLog(r io.Reader) error {
-       b, err := io.ReadAll(r)
-       if err != nil {
-               return err
-       }
-       if string(bytes.TrimSpace(b)) != `FuzzB "seed"` {
-               return fmt.Errorf("coordinator: did not test FuzzB seed")
-       }
-       return nil
-}
-
-func checkWorkerLog(r io.Reader) error {
-       scan := bufio.NewScanner(r)
-       var sawAMutant bool
-       for scan.Scan() {
-               line := scan.Text()
-               if !strings.HasPrefix(line, "FuzzA ") {
-                       return fmt.Errorf("worker: tested something other than target: %s", line)
-               }
-               if strings.TrimPrefix(line, "FuzzA ") != `"seed"` {
-                       sawAMutant = true
-               }
-       }
-       if err := scan.Err(); err != nil && err != bufio.ErrTooLong {
-               return err
-       }
-       if !sawAMutant {
-               return fmt.Errorf("worker: did not test any mutants")
-       }
-       return nil
-}
--- mutator_test.go --
-package fuzz_test
-
-import (
-       "testing"
-)
-
-// TODO(katiehockman): re-work this test once we have a better fuzzing engine
-// (ie. more mutations, and compiler instrumentation)
-func FuzzMutator(f *testing.F) {
-       // TODO(katiehockman): simplify this once we can dedupe crashes (e.g.
-       // replace map with calls to panic, and simply count the number of crashes
-       // that were added to testdata)
-       crashes := make(map[string]bool)
-       // No seed corpus initiated
-       f.Fuzz(func(t *testing.T, b []byte) {
-               crashes[string(b)] = true
-               if len(crashes) >= 10 {
-                       panic("mutator found enough unique mutations")
-               }
-       })
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_mutator_repeat.txt b/src/cmd/go/testdata/script/test_fuzz_mutator_repeat.txt
deleted file mode 100644 (file)
index 3b005c9..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-# TODO(jayconrod): support shared memory on more platforms.
-[!GOOS:darwin] [!GOOS:linux] [!GOOS:windows] skip
-
-# Verify that the fuzzing engine records the actual crashing input, even when
-# a worker process terminates without communicating the crashing input back
-# to the coordinator.
-
-[short] skip
-env GOCACHE=$WORK/cache
-
-# Start fuzzing. The worker crashes after 100 iterations.
-# The fuzz function writes the crashing input to "want" before exiting.
-# The fuzzing engine reconstructs the crashing input and saves it to testdata.
-! exists want
-! go test -fuzz=. -parallel=1 -fuzztime=110x -fuzzminimizetime=10x -v
-stdout '^\s+fuzzing process hung or terminated unexpectedly: exit status'
-stdout 'Failing input written to testdata'
-
-# Run the fuzz target without fuzzing. The fuzz function is called with the
-# crashing input in testdata. The test passes if that input is identical to
-# the one saved in "want".
-exists want
-go test -want=want
-
--- go.mod --
-module fuzz
-
-go 1.17
--- fuzz_test.go --
-package fuzz
-
-import (
-       "bytes"
-       "flag"
-       "os"
-       "testing"
-)
-
-var wantFlag = flag.String("want", "", "file containing previous crashing input")
-
-func FuzzRepeat(f *testing.F) {
-       i := 0
-       f.Fuzz(func(t *testing.T, b []byte) {
-               i++
-               if i == 100 {
-                       f, err := os.OpenFile("want", os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
-                       if err != nil {
-                               // Couldn't create the file. Return without crashing, and try
-                               // again.
-                               i--
-                               t.Skip(err)
-                       }
-                       if _, err := f.Write(b); err != nil {
-                               // We already created the file, so if we failed to write it
-                               // there's not much we can do. The test will fail anyway, but
-                               // at least make sure the error is logged to stdout.
-                               t.Fatal(err)
-                       }
-                       if err := f.Close(); err != nil {
-                               t.Fatal(err)
-                       }
-                       os.Exit(1) // crash without communicating
-               }
-
-               if *wantFlag != "" {
-                       want, err := os.ReadFile(*wantFlag)
-                       if err != nil {
-                               t.Fatal(err)
-                       }
-                       if !bytes.Equal(want, b) {
-                               t.Fatalf("inputs are not equal!\n got: %q\nwant:%q", b, want)
-                       }
-               }
-       })
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_non_crash_signal.txt b/src/cmd/go/testdata/script/test_fuzz_non_crash_signal.txt
deleted file mode 100644 (file)
index 94a0421..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-# NOTE: this test is skipped on Windows, since there's no concept of signals.
-# When a process terminates another process, it provides an exit code.
-[GOOS:windows] skip
-[!fuzz] skip
-[short] skip
-env GOCACHE=$WORK/cache
-
-# FuzzNonCrash sends itself a signal that does not appear to be a crash.
-# We should not save a crasher.
-! go test -fuzz=FuzzNonCrash
-! exists testdata
-! stdout unreachable
-! stderr unreachable
-stdout 'fuzzing process terminated by unexpected signal; no crash will be recorded: signal: terminated'
-
-# FuzzKill sends itself a signal that cannot be caught by the worker process
-# and does not appear to be a crash.
-# We should not save a crasher.
-! go test -fuzz=FuzzKill
-! exists testdata
-! stdout unreachable
-! stderr unreachable
-stdout 'fuzzing process terminated by unexpected signal; no crash will be recorded: signal: killed'
-
-# FuzzCrash sends itself a signal that looks like a crash.
-# We should save a crasher.
-! go test -fuzz=FuzzCrash
-exists testdata/fuzz/FuzzCrash
-stdout '^\s+fuzzing process hung or terminated unexpectedly: exit status'
-
--- go.mod --
-module test
-
-go 1.17
--- fuzz_posix_test.go --
-// +build darwin freebsd linux
-
-package fuzz
-
-import (
-       "syscall"
-       "testing"
-)
-
-func FuzzNonCrash(f *testing.F) {
-       f.Fuzz(func(*testing.T, bool) {
-               pid := syscall.Getpid()
-               if err := syscall.Kill(pid, syscall.SIGTERM); err != nil {
-                       panic(err)
-               }
-               // signal may not be received immediately. Wait for it.
-               select{}
-       })
-}
-
-func FuzzKill(f *testing.F) {
-       f.Fuzz(func(*testing.T, bool) {
-               pid := syscall.Getpid()
-               if err := syscall.Kill(pid, syscall.SIGKILL); err != nil {
-                       panic(err)
-               }
-               // signal may not be received immediately. Wait for it.
-               select{}
-       })
-}
-
-func FuzzCrash(f *testing.F) {
-       f.Fuzz(func(*testing.T, bool) {
-               pid := syscall.Getpid()
-               if err := syscall.Kill(pid, syscall.SIGILL); err != nil {
-                       panic(err)
-               }
-               // signal may not be received immediately. Wait for it.
-               select{}
-       })
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_parallel.txt b/src/cmd/go/testdata/script/test_fuzz_parallel.txt
deleted file mode 100644 (file)
index 8ff965a..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-[!fuzz] skip
-[short] skip
-env GOCACHE=$WORK/cache
-
-# When running seed inputs, T.Parallel should let multiple inputs run in
-# parallel.
-go test -run=FuzzSeed
-
-# When fuzzing, T.Parallel should be safe to call, but it should have no effect.
-# We just check that it doesn't hang, which would be the most obvious
-# failure mode.
-# TODO(jayconrod): check for the string "after T.Parallel". It's not printed
-# by 'go test', so we can't distinguish that crasher from some other panic.
-! go test -run=FuzzMutate -fuzz=FuzzMutate
-exists testdata/fuzz/FuzzMutate
-
-# Testdata should now contain a corpus entry which will fail FuzzMutate.
-# Run the test without fuzzing, setting -parallel to different values to make
-# sure it fails, and doesn't hang.
-! go test -run=FuzzMutate -parallel=1
-! go test -run=FuzzMutate -parallel=2
-! go test -run=FuzzMutate -parallel=4
-
--- go.mod --
-module fuzz_parallel
-
-go 1.17
--- fuzz_parallel_test.go --
-package fuzz_parallel
-
-import (
-       "sort"
-       "sync"
-       "testing"
-)
-
-func FuzzSeed(f *testing.F) {
-       for _, v := range [][]byte{{'a'}, {'b'}, {'c'}} {
-               f.Add(v)
-       }
-
-       var mu sync.Mutex
-       var before, after []byte
-       f.Cleanup(func() {
-               sort.Slice(after, func(i, j int) bool { return after[i] < after[j] })
-               got := string(before) + string(after)
-               want := "abcabc"
-               if got != want {
-                       f.Fatalf("got %q; want %q", got, want)
-               }
-       })
-
-       f.Fuzz(func(t *testing.T, b []byte) {
-               before = append(before, b...)
-               t.Parallel()
-               mu.Lock()
-               after = append(after, b...)
-               mu.Unlock()
-       })
-}
-
-func FuzzMutate(f *testing.F) {
-       f.Fuzz(func(t *testing.T, _ []byte) {
-               t.Parallel()
-               t.Error("after T.Parallel")
-       })
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_profile_flags.txt b/src/cmd/go/testdata/script/test_fuzz_profile_flags.txt
deleted file mode 100644 (file)
index 5434c72..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-[!fuzz] skip
-
-! go test -fuzz=FuzzTrivial -coverprofile=prof
-! stdout .
-stderr '^cannot use -coverprofile flag with -fuzz flag$'
-
-! go test -fuzz=FuzzTrivial -blockprofile=prof
-! stdout .
-stderr '^cannot use -blockprofile flag with -fuzz flag$'
-
-! go test -fuzz=FuzzTrivial -cpuprofile=prof
-! stdout .
-stderr '^cannot use -cpuprofile flag with -fuzz flag$'
-
-! go test -fuzz=FuzzTrivial -memprofile=prof
-! stdout .
-stderr '^cannot use -memprofile flag with -fuzz flag$'
-
-! go test -fuzz=FuzzTrivial -mutexprofile=prof
-! stdout .
-stderr '^cannot use -mutexprofile flag with -fuzz flag$'
-
-! go test -fuzz=FuzzTrivial -trace=prof
-! stdout .
-stderr '^cannot use -trace flag with -fuzz flag$'
-
--- go.mod --
-module example
-
-go 1.18
--- fuzz_test.go --
-package example
-
-import "testing"
-
-func FuzzTrivial(f *testing.F) {
-       f.Fuzz(func(t *testing.T, _ []byte) {})
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_return.txt b/src/cmd/go/testdata/script/test_fuzz_return.txt
deleted file mode 100644 (file)
index c0540ef..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-[short] skip
-
-# Disable vet, as its "tests" analyzer would report the same problem statically.
-
-! go test -vet=off .
-stdout '^panic: testing: fuzz target must not return a value \[recovered, repanicked\]$'
-
--- go.mod --
-module test
-go 1.18
--- x_test.go --
-package test
-
-import "testing"
-
-func Fuzz_returnErr(f *testing.F) {
-       f.Add("hello, validation!")
-       f.Fuzz(func(t *testing.T, in string) string {
-               return in
-       })
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_run.txt b/src/cmd/go/testdata/script/test_fuzz_run.txt
deleted file mode 100644 (file)
index 99a4413..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-[!fuzz] skip
-[short] skip
-env GOCACHE=$WORK/cache
-
-# Tests which verify the behavior and command line output when
-# running a fuzz target as a unit test.
-
-# Tests without -run.
-
-! go test
-stdout FAIL
-stdout 'error here'
-
-! go test -v
-stdout FAIL
-stdout 'error here'
-stdout '=== RUN   FuzzFoo/thisfails'
-stdout '--- FAIL: FuzzFoo/thisfails'
-stdout '=== RUN   FuzzFoo/thispasses'
-stdout '--- PASS: FuzzFoo/thispasses'
-
-# Tests where -run matches all seed corpora.
-
-! go test -run FuzzFoo/this
-stdout FAIL
-stdout 'error here'
-! stdout 'no tests to run'
-
-! go test -run /this
-stdout FAIL
-stdout 'error here'
-! stdout 'no tests to run'
-
-! go test -v -run FuzzFoo/this
-stdout FAIL
-stdout 'error here'
-stdout '=== RUN   FuzzFoo/thisfails'
-stdout '--- FAIL: FuzzFoo/thisfails'
-stdout '=== RUN   FuzzFoo/thispasses'
-stdout '--- PASS: FuzzFoo/thispasses'
-! stdout 'no tests to run'
-
-! go test -v -run /this
-stdout FAIL
-stdout 'error here'
-stdout '=== RUN   FuzzFoo/thisfails'
-stdout '--- FAIL: FuzzFoo/thisfails'
-stdout '=== RUN   FuzzFoo/thispasses'
-stdout '--- PASS: FuzzFoo/thispasses'
-! stdout 'no tests to run'
-
-# Tests where -run only matches one seed corpus which passes.
-
-go test -run FuzzFoo/thispasses
-stdout ok
-! stdout 'no tests to run'
-
-go test -run /thispasses
-stdout ok
-! stdout 'no tests to run'
-
-# Same tests in verbose mode
-go test -v -run FuzzFoo/thispasses
-stdout '=== RUN   FuzzFoo/thispasses'
-stdout '--- PASS: FuzzFoo/thispasses'
-! stdout '=== RUN   FuzzFoo/thisfails'
-! stdout 'no tests to run'
-
-go test -v -run /thispasses
-stdout '=== RUN   FuzzFoo/thispasses'
-stdout '--- PASS: FuzzFoo/thispasses'
-! stdout '=== RUN   FuzzFoo/thisfails'
-! stdout 'no tests to run'
-
-# Tests where -run only matches one seed corpus which fails.
-
-! go test -run FuzzFoo/thisfails
-stdout FAIL
-stdout 'error here'
-! stdout 'no tests to run'
-
-! go test -run /thisfails
-stdout FAIL
-stdout 'error here'
-! stdout 'no tests to run'
-
-! go test -v -run FuzzFoo/thisfails
-stdout 'error here'
-stdout '=== RUN   FuzzFoo/thisfails'
-stdout '--- FAIL: FuzzFoo/thisfails'
-! stdout '=== RUN   FuzzFoo/thispasses'
-! stdout 'no tests to run'
-
-! go test -v -run /thisfails
-stdout 'error here'
-stdout '=== RUN   FuzzFoo/thisfails'
-stdout '--- FAIL: FuzzFoo/thisfails'
-! stdout '=== RUN   FuzzFoo/thispasses'
-! stdout 'no tests to run'
-
-# Tests where -run doesn't match any seed corpora.
-
-go test -run FuzzFoo/nomatch
-stdout ok
-
-go test -run /nomatch
-stdout ok
-
-go test -v -run FuzzFoo/nomatch
-stdout '=== RUN   FuzzFoo'
-stdout '--- PASS: FuzzFoo'
-stdout ok
-! stdout 'no tests to run'
-
-go test -v -run /nomatch
-stdout '=== RUN   FuzzFoo'
-stdout '--- PASS: FuzzFoo'
-stdout ok
-! stdout 'no tests to run'
-
--- go.mod --
-module example.com/x
-
-go 1.16
--- x_test.go --
-package x
-
-import "testing"
-
-func FuzzFoo(f *testing.F) {
-    f.Add("this is fine")
-    f.Fuzz(func(t *testing.T, s string) {
-        if s == "fails" {
-            t.Error("error here")
-        }
-    })
-}
--- testdata/fuzz/FuzzFoo/thisfails --
-go test fuzz v1
-string("fails")
--- testdata/fuzz/FuzzFoo/thispasses --
-go test fuzz v1
-string("passes")
diff --git a/src/cmd/go/testdata/script/test_fuzz_seed_corpus.txt b/src/cmd/go/testdata/script/test_fuzz_seed_corpus.txt
deleted file mode 100644 (file)
index 8a7a24a..0000000
+++ /dev/null
@@ -1,203 +0,0 @@
-[!fuzz-instrumented] skip
-[short] skip
-env GOCACHE=$WORK/cache
-
-# Test that fuzzing a target with a failure in f.Add prints the crash
-# and doesn't write anything to testdata/fuzz
-! go test -fuzz=^FuzzWithAdd$ -run=^FuzzWithAdd$ -fuzztime=1x
-! stdout ^ok
-! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithAdd[/\\]'
-stdout FAIL
-
-# Test that fuzzing a target with a success in f.Add and a fuzztime of only
-# 1 does not produce a crash.
-go test -fuzz=FuzzWithGoodAdd -run=FuzzWithGoodAdd -fuzztime=1x
-stdout ok
-! stdout FAIL
-
-# Test that fuzzing a target with a failure in testdata/fuzz prints the crash
-# and doesn't write anything to testdata/fuzz
-! go test -fuzz=FuzzWithTestdata -run=FuzzWithTestdata -fuzztime=1x
-! stdout ^ok
-! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithTestdata[/\\]'
-stdout 'failure while testing seed corpus entry: FuzzWithTestdata/1'
-stdout FAIL
-
-# Test that fuzzing a target with no seed corpus or cache finds a crash, prints
-# it, and write it to testdata
-! go test -fuzz=FuzzWithNoCache -run=FuzzWithNoCache -fuzztime=1x
-! stdout ^ok
-stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithNoCache[/\\]'
-stdout FAIL
-
-# Write a crashing input to the cache
-mkdir $GOCACHE/fuzz/example.com/x/FuzzWithCache
-cp cache-file $GOCACHE/fuzz/example.com/x/FuzzWithCache/1
-
-# Test that fuzzing a target with a failure in the cache prints the crash
-# and writes this as a "new" crash to testdata/fuzz
-! go test -fuzz=FuzzWithCache -run=FuzzWithCache -fuzztime=1x
-! stdout ^ok
-stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithCache[/\\]'
-stdout FAIL
-
-# Write a crashing input to the cache
-mkdir $GOCACHE/fuzz/example.com/x/FuzzWithMinimizableCache
-cp cache-file-bytes $GOCACHE/fuzz/example.com/x/FuzzWithMinimizableCache/1
-
-# Test that fuzzing a target with a failure in the cache minimizes it and writes
-# the new crash to testdata/fuzz
-! go test -fuzz=FuzzWithMinimizableCache -run=FuzzWithMinimizableCache -fuzztime=10000x
-! stdout ^ok
-stdout 'gathering baseline coverage'
-stdout 'got the minimum size!'
-stdout 'contains a non-zero byte of length 10'
-stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithMinimizableCache[/\\]'
-stdout FAIL
-# Make sure this crash didn't come from fuzzing
-# (the log line that states fuzzing began shouldn't have printed)
-! stdout 'execs'
-
-# Clear the fuzz cache and make sure it's gone
-go clean -fuzzcache
-! exists $GOCACHE/fuzz
-
-# The tests below should operate the exact same as the previous tests. If -fuzz
-# is enabled, then whatever target is going to be fuzzed shouldn't be run by
-# anything other than the workers.
-
-# Test that fuzzing a target (with -run=None set) with a failure in f.Add prints
-# the crash and doesn't write anything to testdata/fuzz -fuzztime=1x
-! go test -fuzz=^FuzzWithAdd$ -run=None
-! stdout ^ok
-! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithAdd[/\\]'
-stdout FAIL
-
-# Test that fuzzing a target (with -run=None set) with a success in f.Add and a
-# fuzztime of only 1 does not produce a crash.
-go test -fuzz=FuzzWithGoodAdd -run=None -fuzztime=1x
-stdout ok
-! stdout FAIL
-
-# Test that fuzzing a target (with -run=None set) with a failure in
-# testdata/fuzz prints the crash and doesn't write anything to testdata/fuzz
-! go test -fuzz=FuzzWithTestdata -run=None -fuzztime=1x
-! stdout ^ok
-! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithTestdata[/\\]'
-stdout FAIL
-
-# Write a crashing input to the cache
-mkdir $GOCACHE/fuzz/example.com/x/FuzzRunNoneWithCache
-cp cache-file $GOCACHE/fuzz/example.com/x/FuzzRunNoneWithCache/1
-
-# Test that fuzzing a target (with -run=None set) with a failure in the cache
-# prints the crash and writes this as a "new" crash to testdata/fuzz
-! go test -fuzz=FuzzRunNoneWithCache -run=None -fuzztime=1x
-! stdout ^ok
-stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzRunNoneWithCache[/\\]'
-stdout FAIL
-
-# Clear the fuzz cache and make sure it's gone
-go clean -fuzzcache
-! exists $GOCACHE/fuzz
-
-# The tests below should operate the exact same way for the previous tests with
-# a seed corpus (namely, they should still fail). However, the binary is built
-# without instrumentation, so this should be a "testing only" run which executes
-# the seed corpus before attempting to fuzz.
-
-go test -c
-! exec ./x.test$GOEXE -test.fuzz=^FuzzWithAdd$ -test.run=^FuzzWithAdd$ -test.fuzztime=1x -test.fuzzcachedir=$WORK/cache
-! stdout ^ok
-! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithAdd[/\\]'
-stdout FAIL
-stderr warning
-
-go test -c
-! exec ./x.test$GOEXE -test.fuzz=FuzzWithTestdata -test.run=^FuzzWithTestdata$ -test.fuzztime=1x -test.fuzzcachedir=$WORK/cache
-! stdout ^ok
-! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithTestdata[/\\]'
-stdout FAIL
-stderr warning
-
--- go.mod --
-module example.com/x
-
-go 1.16
--- x_test.go --
-package x
-
-import "testing"
-
-func FuzzWithAdd(f *testing.F) {
-    f.Add(10)
-    f.Fuzz(func(t *testing.T, i int) {
-        if i == 10 {
-            t.Error("bad thing here")
-        }
-    })
-}
-
-func FuzzWithGoodAdd(f *testing.F) {
-    f.Add(10)
-    f.Fuzz(func(t *testing.T, i int) {
-        if i != 10 {
-            t.Error("bad thing here")
-        }
-    })
-}
-
-func FuzzWithTestdata(f *testing.F) {
-    f.Fuzz(func(t *testing.T, i int) {
-        if i == 10 {
-            t.Error("bad thing here")
-        }
-    })
-}
-
-func FuzzWithNoCache(f *testing.F) {
-    f.Fuzz(func(t *testing.T, i int) {
-        t.Error("bad thing here")
-    })
-}
-
-func FuzzWithCache(f *testing.F) {
-    f.Fuzz(func(t *testing.T, i int) {
-        if i == 10 {
-            t.Error("bad thing here")
-        }
-    })
-}
-
-func FuzzWithMinimizableCache(f *testing.F) {
-    f.Fuzz(func(t *testing.T, b []byte) {
-               if len(b) < 10 {
-                       return
-               }
-               for _, n := range b {
-                       if n != 0 {
-                               if len(b) == 10 {
-                                       t.Log("got the minimum size!")
-                               }
-                               t.Fatalf("contains a non-zero byte of length %d", len(b))
-                       }
-               }
-    })
-}
-
-func FuzzRunNoneWithCache(f *testing.F) {
-    f.Fuzz(func(t *testing.T, i int) {
-        if i == 10 {
-            t.Error("bad thing here")
-        }
-    })
-}
--- testdata/fuzz/FuzzWithTestdata/1 --
-go test fuzz v1
-int(10)
--- cache-file --
-go test fuzz v1
-int(10)
--- cache-file-bytes --
-go test fuzz v1
-[]byte("11111111111111111111")
diff --git a/src/cmd/go/testdata/script/test_fuzz_setenv.txt b/src/cmd/go/testdata/script/test_fuzz_setenv.txt
deleted file mode 100644 (file)
index 1370cd8..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-[!fuzz] skip
-[short] skip
-env GOCACHE=$WORK/cache
-
-go test -fuzz=FuzzA -fuzztime=100x fuzz_setenv_test.go
-
--- fuzz_setenv_test.go --
-package fuzz
-
-import (
-  "flag"
-  "os"
-  "testing"
-)
-
-func FuzzA(f *testing.F) {
-  if s := os.Getenv("TEST_FUZZ_SETENV_A"); isWorker() && s == "" {
-    f.Fatal("environment variable not set")
-  } else if !isWorker() && s != "" {
-    f.Fatal("environment variable already set")
-  }
-  f.Setenv("TEST_FUZZ_SETENV_A", "A")
-  if os.Getenv("TEST_FUZZ_SETENV_A") == "" {
-    f.Fatal("Setenv did not set environment variable")
-  }
-  f.Fuzz(func(*testing.T, []byte) {})
-}
-
-func FuzzB(f *testing.F) {
-  if os.Getenv("TEST_FUZZ_SETENV_A") != "" {
-    f.Fatal("environment variable not cleared after FuzzA")
-  }
-  f.Skip()
-}
-
-func isWorker() bool {
-       f := flag.Lookup("test.fuzzworker")
-       if f == nil {
-               return false
-       }
-       get, ok := f.Value.(flag.Getter)
-       if !ok {
-               return false
-       }
-       return get.Get() == interface{}(true)
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_test_race.txt b/src/cmd/go/testdata/script/test_fuzz_test_race.txt
deleted file mode 100644 (file)
index 1bed47d..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-# Test that when both race detection and coverage instrumentation are enabled,
-# and seed values are being executed, the race detector isn't mistakenly
-# triggered.
-
-[short] skip
-[!fuzz] skip
-[!race] skip
-env GOCACHE=$WORK/cache
-
-# Test with coverage instrumentation enabled (-fuzz) and race instrumentation
-# but without actually fuzzing the target (by using a non-matching pattern)
-go test -fuzz=xxx -race -v
-! stderr 'race detected during execution of test'
-
-# Test with just race instrumentation enabled
-go test -race -v
-! stderr 'race detected during execution of test'
-
-# Test with coverage and race instrumentation enabled, and a matching fuzz
-# pattern
-go test -fuzz=FuzzRace -race -v -fuzztime=200x
-! stderr 'race detected during execution of test'
-
--- go.mod --
-module test
-
--- race_test.go --
-package race
-
-import "testing"
-
-func FuzzRace(f *testing.F) {
-       for i := 0; i < 100; i++ {
-               f.Add(i)
-       }
-
-       f.Fuzz(func(t *testing.T, i int) {
-               t.Parallel()
-       })
-}
diff --git a/src/cmd/go/testdata/script/test_fuzz_unsupported.txt b/src/cmd/go/testdata/script/test_fuzz_unsupported.txt
deleted file mode 100644 (file)
index 1ed0b8a..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-[fuzz] skip
-
-! go test -fuzz=. -fuzztime=1x
-! stdout .
-stderr '^-fuzz flag is not supported on '$GOOS'/'$GOARCH'$'
-
--- go.mod --
-module example
-
-go 1.18
--- fuzz_test.go --
-package example
-
-import "testing"
-
-func FuzzTrivial(f *testing.F) {
-       f.Fuzz(func(t *testing.T, _ []byte) {})
-}
diff --git a/src/cmd/internal/fuzztest/script_test.go b/src/cmd/internal/fuzztest/script_test.go
new file mode 100644 (file)
index 0000000..e8fe701
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2025 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 fuzztest
+
+import (
+       "cmd/internal/script/scripttest"
+       "flag"
+       "internal/testenv"
+       "testing"
+)
+
+//go:generate go test cmd/internal/fuzztest -v -run=TestScript/README --fixreadme
+
+var fixReadme = flag.Bool("fixreadme", false, "if true, update README for script tests")
+
+func TestScript(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+       testenv.SkipIfShortAndSlow(t)
+       scripttest.RunToolScriptTest(t, nil, "testdata/script", *fixReadme)
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/README b/src/cmd/internal/fuzztest/testdata/script/README
new file mode 100644 (file)
index 0000000..9ec997a
--- /dev/null
@@ -0,0 +1,286 @@
+This file is generated by 'go generate'. DO NOT EDIT.
+
+This directory holds test scripts *.txt run during 'go test cmd/<toolname>'.
+To run a specific script foo.txt
+
+       go test cmd/<toolname> -run=Script/^foo$
+
+In general script files should have short names: a few words,
+ not whole sentences.
+The first word should be the general category of behavior being tested,
+often the name of a go subcommand (build, link, compile, ...) or concept (vendor, pattern).
+
+Each script is a text archive (go doc internal/txtar).
+The script begins with an actual command script to run
+followed by the content of zero or more supporting files to
+create in the script's temporary file system before it starts executing.
+
+As an example, run_hello.txt says:
+
+       # hello world
+       go run hello.go
+       stderr 'hello world'
+       ! stdout .
+
+       -- hello.go --
+       package main
+       func main() { println("hello world") }
+
+Each script runs in a fresh temporary work directory tree, available to scripts as $WORK.
+Scripts also have access to other environment variables, including:
+
+       GOARCH=<target GOARCH>
+       GOOS=<target GOOS>
+       TMPDIR=$WORK/tmp
+       devnull=<value of os.DevNull>
+       goversion=<current Go version; for example, 1.12>
+
+On Plan 9, the variables $path and $home are set instead of $PATH and $HOME.
+On Windows, the variables $USERPROFILE and $TMP are set instead of
+$HOME and $TMPDIR.
+
+The lines at the top of the script are a sequence of commands to be executed by
+a small script engine configured in .../cmd/internal/script/scripttest/run.go (not the system shell).
+
+Each line of a script is parsed into a sequence of space-separated command
+words, with environment variable expansion within each word and # marking
+an end-of-line comment. Additional variables named ':' and '/' are expanded
+within script arguments (expanding to the value of os.PathListSeparator and
+os.PathSeparator respectively) but are not inherited in subprocess environments.
+
+Adding single quotes around text keeps spaces in that text from being treated
+as word separators and also disables environment variable expansion. Inside a
+single-quoted block of text, a repeated single quote indicates a literal single
+quote, as in:
+
+    'Don''t communicate by sharing memory.'
+
+A line beginning with # is a comment and conventionally explains what is being
+done or tested at the start of a new section of the script.
+
+Commands are executed one at a time, and errors are checked for each command;
+if any command fails unexpectedly, no subsequent commands in the script are
+executed. The command prefix ! indicates that the command on the rest of the
+line (typically go or a matching predicate) must fail instead of succeeding.
+The command prefix ? indicates that the command may or may not succeed, but the
+script should continue regardless.
+
+The command prefix [cond] indicates that the command on the rest of the line
+should only run when the condition is satisfied.
+
+A condition can be negated: [!root] means to run the rest of the line only if
+the user is not root. Multiple conditions may be given for a single command,
+for example, '[linux] [amd64] skip'. The command will run if all conditions are
+satisfied.
+
+When TestScript runs a script and the script fails, by default TestScript shows
+the execution of the most recent phase of the script (since the last # comment)
+and only shows the # comments for earlier phases.
+
+Note also that in reported output, the actual name of the per-script temporary directory
+has been consistently replaced with the literal string $WORK.
+
+The available commands are:
+cat files...
+       concatenate files and print to the script's stdout buffer
+
+
+cc args...
+       run the platform C compiler
+
+
+cd dir
+       change the working directory
+
+
+chmod perm paths...
+       change file mode bits
+
+       Changes the permissions of the named files or directories to
+       be equal to perm.
+       Only numerical permissions are supported.
+
+cmp [-q] file1 file2
+       compare files for differences
+
+       By convention, file1 is the actual data and file2 is the
+       expected data.
+       The command succeeds if the file contents are identical.
+       File1 can be 'stdout' or 'stderr' to compare the stdout or
+       stderr buffer from the most recent command.
+
+cmpenv [-q] file1 file2
+       compare files for differences, with environment expansion
+
+       By convention, file1 is the actual data and file2 is the
+       expected data.
+       The command succeeds if the file contents are identical
+       after substituting variables from the script environment.
+       File1 can be 'stdout' or 'stderr' to compare the script's
+       stdout or stderr buffer.
+
+cp src... dst
+       copy files to a target file or directory
+
+       src can include 'stdout' or 'stderr' to copy from the
+       script's stdout or stderr buffer.
+
+echo string...
+       display a line of text
+
+
+env [key[=value]...]
+       set or log the values of environment variables
+
+       With no arguments, print the script environment to the log.
+       Otherwise, add the listed key=value pairs to the environment
+       or print the listed keys.
+
+exec program [args...] [&]
+       run an executable program with arguments
+
+       Note that 'exec' does not terminate the script (unlike Unix
+       shells).
+
+exists [-readonly] [-exec] file...
+       check that files exist
+
+
+go [args...] [&]
+       run the 'go' program provided by the script host
+
+
+grep [-count=N] [-q] 'pattern' file
+       find lines in a file that match a pattern
+
+       The command succeeds if at least one match (or the exact
+       count, if given) is found.
+       The -q flag suppresses printing of matches.
+
+help [-v] name...
+       log help text for commands and conditions
+
+       To display help for a specific condition, enclose it in
+       brackets: 'help [amd64]'.
+       To display complete documentation when listing all commands,
+       pass the -v flag.
+
+mkdir path...
+       create directories, if they do not already exist
+
+       Unlike Unix mkdir, parent directories are always created if
+       needed.
+
+mv old new
+       rename a file or directory to a new path
+
+       OS-specific restrictions may apply when old and new are in
+       different directories.
+
+replace [old new]... file
+       replace strings in a file
+
+       The 'old' and 'new' arguments are unquoted as if in quoted
+       Go strings.
+
+rm path...
+       remove a file or directory
+
+       If the path is a directory, its contents are removed
+       recursively.
+
+skip [msg]
+       skip the current test
+
+
+sleep duration [&]
+       sleep for a specified duration
+
+       The duration must be given as a Go time.Duration string.
+
+stderr [-count=N] [-q] 'pattern' file
+       find lines in the stderr buffer that match a pattern
+
+       The command succeeds if at least one match (or the exact
+       count, if given) is found.
+       The -q flag suppresses printing of matches.
+
+stdout [-count=N] [-q] 'pattern' file
+       find lines in the stdout buffer that match a pattern
+
+       The command succeeds if at least one match (or the exact
+       count, if given) is found.
+       The -q flag suppresses printing of matches.
+
+stop [msg]
+       stop execution of the script
+
+       The message is written to the script log, but no error is
+       reported from the script engine.
+
+symlink path -> target
+       create a symlink
+
+       Creates path as a symlink to target.
+       The '->' token (like in 'ls -l' output on Unix) is required.
+
+wait 
+       wait for completion of background commands
+
+       Waits for all background commands to complete.
+       The output (and any error) from each command is printed to
+       the log in the order in which the commands were started.
+       After the call to 'wait', the script's stdout and stderr
+       buffers contain the concatenation of the background
+       commands' outputs.
+
+
+
+The available conditions are:
+[GOARCH:*]
+       runtime.GOARCH == <suffix>
+[GODEBUG:*]
+       GODEBUG contains <suffix>
+[GOEXPERIMENT:*]
+       GOEXPERIMENT <suffix> is enabled
+[GOOS:*]
+       runtime.GOOS == <suffix>
+[asan]
+       GOOS/GOARCH supports -asan
+[buildmode:*]
+       go supports -buildmode=<suffix>
+[cgo]
+       host CGO_ENABLED
+[cgolinkext]
+       platform requires external linking for cgo
+[compiler:*]
+       runtime.Compiler == <suffix>
+[cross]
+       cmd/go GOOS/GOARCH != GOHOSTOS/GOHOSTARCH
+[exec:*]
+       <suffix> names an executable in the test binary's PATH
+[fuzz]
+       GOOS/GOARCH supports -fuzz
+[fuzz-instrumented]
+       GOOS/GOARCH supports -fuzz with instrumentation
+[go-builder]
+       GO_BUILDER_NAME is non-empty
+[link]
+       testenv.HasLink()
+[msan]
+       GOOS/GOARCH supports -msan
+[mustlinkext]
+       platform always requires external linking
+[pielinkext]
+       platform requires external linking for PIE
+[race]
+       GOOS/GOARCH supports -race
+[root]
+       os.Geteuid() == 0
+[short]
+       testing.Short()
+[symlink]
+       testenv.HasSymlink()
+[verbose]
+       testing.Verbose()
+
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz.txt
new file mode 100644 (file)
index 0000000..bb88ead
--- /dev/null
@@ -0,0 +1,503 @@
+[!fuzz] skip
+[short] skip
+env GOCACHE=$WORK/cache
+
+# This test uses -vet=off to suppress vet, as vet's "tests" analyzer would
+# otherwise statically report the problems we are trying to observe dynamically.
+
+# Test that running a fuzz target that returns without failing or calling
+# f.Fuzz fails and causes a non-zero exit status.
+! go test -vet=off noop_fuzz_test.go
+! stdout ^ok
+stdout FAIL
+
+# Test that fuzzing a fuzz target that returns without failing or calling
+# f.Fuzz fails and causes a non-zero exit status.
+! go test -vet=off -fuzz=Fuzz -fuzztime=1x noop_fuzz_test.go
+! stdout ^ok
+stdout FAIL
+
+# Test that calling f.Error in a fuzz target causes a non-zero exit status.
+! go test -vet=off -fuzz=Fuzz -fuzztime=1x error_fuzz_test.go
+! stdout ^ok
+stdout FAIL
+
+# Test that calling f.Fatal in a fuzz target causes a non-zero exit status.
+! go test -vet=off fatal_fuzz_test.go
+! stdout ^ok
+stdout FAIL
+
+# Test that successful test exits cleanly.
+go test -vet=off success_fuzz_test.go
+stdout ^ok
+! stdout FAIL
+
+# Test that successful fuzzing exits cleanly.
+go test -vet=off -fuzz=Fuzz -fuzztime=1x success_fuzz_test.go
+stdout ok
+! stdout FAIL
+
+# Test that calling f.Fatal while fuzzing causes a non-zero exit status.
+! go test -vet=off -fuzz=Fuzz -fuzztime=1x fatal_fuzz_test.go
+! stdout ^ok
+stdout FAIL
+
+# Test error with seed corpus in f.Fuzz
+! go test -vet=off -run Fuzz_error -vet=off fuzz_add_test.go
+! stdout ^ok
+stdout FAIL
+stdout 'error here'
+
+[short] stop
+
+# Test that calling panic(nil) in a fuzz target causes a non-zero exit status.
+! go test -vet=off panic_fuzz_test.go
+! stdout ^ok
+stdout FAIL
+
+# Test that skipped test exits cleanly.
+go test -vet=off skipped_fuzz_test.go
+stdout ok
+! stdout FAIL
+
+# Test that f.Fatal within f.Fuzz panics
+! go test -vet=off fatal_fuzz_fn_fuzz_test.go
+! stdout ^ok
+! stdout 'fatal here'
+stdout FAIL
+stdout 'fuzz target'
+
+# Test that f.Error within f.Fuzz panics
+! go test -vet=off error_fuzz_fn_fuzz_test.go
+! stdout ^ok
+! stdout 'error here'
+stdout FAIL
+stdout 'fuzz target'
+
+# Test that f.Fail within f.Fuzz panics
+! go test -vet=off fail_fuzz_fn_fuzz_test.go
+! stdout ^ok
+stdout FAIL
+stdout 'fuzz target'
+
+# Test that f.Skip within f.Fuzz panics
+! go test -vet=off skip_fuzz_fn_fuzz_test.go
+! stdout ^ok
+! stdout 'skip here'
+stdout FAIL
+stdout 'fuzz target'
+
+# Test that f.Skipped within f.Fuzz panics
+! go test -vet=off skipped_fuzz_fn_fuzz_test.go
+! stdout ^ok
+! stdout 'f.Skipped is'
+stdout FAIL
+stdout 'fuzz target'
+stdout 't.Skipped is false'
+
+# Test that runtime.Goexit within the fuzz function is an error.
+! go test -vet=off goexit_fuzz_fn_fuzz_test.go
+! stdout ^ok
+stdout FAIL
+
+# Test that a call to f.Fatal after the Fuzz func is executed.
+! go test -vet=off fatal_after_fuzz_func_fuzz_test.go
+! stdout ok
+stdout FAIL
+
+# Test that missing *T in f.Fuzz causes a non-zero exit status.
+! go test -vet=off incomplete_fuzz_call_fuzz_test.go
+! stdout ^ok
+stdout FAIL
+
+# Test that a panic in the Cleanup func is executed.
+! go test -vet=off cleanup_fuzz_test.go
+! stdout ^ok
+stdout FAIL
+stdout 'failed some precondition'
+
+# Test success with seed corpus in f.Fuzz
+go test -vet=off -run Fuzz_pass -vet=off fuzz_add_test.go
+stdout ok
+! stdout FAIL
+! stdout 'off by one error'
+
+# Test fatal with seed corpus in f.Fuzz
+! go test -vet=off -run Fuzz_fatal -vet=off fuzz_add_test.go
+! stdout ^ok
+stdout FAIL
+stdout 'fatal here'
+
+# Test panic with seed corpus in f.Fuzz
+! go test -vet=off -run Fuzz_panic -vet=off fuzz_add_test.go
+! stdout ^ok
+stdout FAIL
+stdout 'off by one error'
+
+# Test panic(nil) with seed corpus in f.Fuzz
+! go test -vet=off -run Fuzz_nilPanic -vet=off fuzz_add_test.go
+! stdout ^ok
+stdout FAIL
+
+# Test panic with unsupported seed corpus
+! go test -vet=off -run Fuzz_unsupported -vet=off fuzz_add_test.go
+! stdout ^ok
+stdout FAIL
+
+# Test panic with different number of args to f.Add
+! go test -vet=off -run Fuzz_addDifferentNumber -vet=off fuzz_add_test.go
+! stdout ^ok
+stdout FAIL
+
+# Test panic with different type of args to f.Add
+! go test -vet=off -run Fuzz_addDifferentType -vet=off fuzz_add_test.go
+! stdout ^ok
+stdout FAIL
+
+# Test that the wrong type given with f.Add will fail.
+! go test -vet=off -run Fuzz_wrongType -vet=off fuzz_add_test.go
+! stdout ^ok
+stdout '\[string int\], want \[\[\]uint8 int8\]'
+stdout FAIL
+
+# Test fatal with testdata seed corpus
+! go test -vet=off -run Fuzz_fail corpustesting/fuzz_testdata_corpus_test.go
+! stdout ^ok
+stdout FAIL
+stdout 'fatal here'
+
+# Test pass with testdata seed corpus
+go test -vet=off -run Fuzz_pass corpustesting/fuzz_testdata_corpus_test.go
+stdout ok
+! stdout FAIL
+! stdout 'fatal here'
+
+# Test pass with testdata and f.Add seed corpus
+go test -vet=off -run Fuzz_passString corpustesting/fuzz_testdata_corpus_test.go
+stdout ok
+! stdout FAIL
+
+# Fuzzing pass with testdata and f.Add seed corpus (skip running tests first)
+go test -vet=off -run=None -fuzz=Fuzz_passString corpustesting/fuzz_testdata_corpus_test.go -fuzztime=10x
+stdout ok
+! stdout FAIL
+
+# Fuzzing pass with testdata and f.Add seed corpus
+go test -vet=off -run=Fuzz_passString -fuzz=Fuzz_passString corpustesting/fuzz_testdata_corpus_test.go -fuzztime=10x
+stdout ok
+! stdout FAIL
+
+# Test panic with malformed seed corpus
+! go test -vet=off -run Fuzz_fail corpustesting/fuzz_testdata_corpus_test.go
+! stdout ^ok
+stdout FAIL
+
+# Test pass with file in other nested testdata directory
+go test -vet=off -run Fuzz_inNestedDir corpustesting/fuzz_testdata_corpus_test.go
+stdout ok
+! stdout FAIL
+! stdout 'fatal here'
+
+# Test fails with file containing wrong type
+! go test -vet=off -run Fuzz_wrongType corpustesting/fuzz_testdata_corpus_test.go
+! stdout ^ok
+stdout FAIL
+
+-- noop_fuzz_test.go --
+package noop_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {}
+
+-- error_fuzz_test.go --
+package error_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Error("error in target")
+}
+
+-- fatal_fuzz_test.go --
+package fatal_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Fatal("fatal in target")
+}
+
+-- panic_fuzz_test.go --
+package panic_fuzz
+
+import "testing"
+
+func Fuzz_panic(f *testing.F) {
+    panic(nil)
+}
+
+-- success_fuzz_test.go --
+package success_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Fuzz(func (*testing.T, []byte) {})
+}
+
+-- skipped_fuzz_test.go --
+package skipped_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Skip()
+}
+
+-- fatal_fuzz_fn_fuzz_test.go --
+package fatal_fuzz_fn_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Add([]byte("aa"))
+    f.Fuzz(func(t *testing.T, b []byte) {
+        f.Fatal("fatal here")
+    })
+}
+
+-- error_fuzz_fn_fuzz_test.go --
+package error_fuzz_fn_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Add([]byte("aa"))
+    f.Fuzz(func(t *testing.T, b []byte) {
+        f.Error("error here")
+    })
+}
+
+-- fail_fuzz_fn_fuzz_test.go --
+package skip_fuzz_fn_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Add([]byte("aa"))
+    f.Fuzz(func(t *testing.T, b []byte) {
+        f.Fail()
+    })
+}
+
+-- skip_fuzz_fn_fuzz_test.go --
+package skip_fuzz_fn_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Add([]byte("aa"))
+    f.Fuzz(func(t *testing.T, b []byte) {
+        f.Skip("skip here")
+    })
+}
+
+-- skipped_fuzz_fn_fuzz_test.go --
+package skipped_fuzz_fn_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Add([]byte("aa"))
+    f.Fuzz(func(t *testing.T, b []byte) {
+        t.Logf("t.Skipped is %t\n", t.Skipped())
+        t.Logf("f.Skipped is %t\n", f.Skipped())
+    })
+}
+
+-- goexit_fuzz_fn_fuzz_test.go --
+package goexit_fuzz_fn_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Add([]byte("aa"))
+    f.Fuzz(func(t *testing.T, b []byte) {
+        runtime.Goexit()
+    })
+}
+
+-- fatal_after_fuzz_func_fuzz_test.go --
+package fatal_after_fuzz_func_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Fuzz(func(t *testing.T, b []byte) {
+        // no-op
+    })
+    f.Fatal("this shouldn't be called")
+}
+
+-- incomplete_fuzz_call_fuzz_test.go --
+package incomplete_fuzz_call_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Fuzz(func(b []byte) {
+        // this is missing *testing.T as the first param, so should panic
+    })
+}
+
+-- cleanup_fuzz_test.go --
+package cleanup_fuzz_test
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Cleanup(func() {
+        panic("failed some precondition")
+    })
+    f.Fuzz(func(t *testing.T, b []byte) {
+        // no-op
+    })
+}
+
+-- fuzz_add_test.go --
+package fuzz_add
+
+import "testing"
+
+func add(f *testing.F) {
+    f.Helper()
+    f.Add([]byte("123"))
+    f.Add([]byte("12345"))
+    f.Add([]byte(""))
+}
+
+func Fuzz_pass(f *testing.F) {
+    add(f)
+    f.Fuzz(func(t *testing.T, b []byte) {
+        if len(b) == -1 {
+            t.Fatal("fatal here") // will not be executed
+        }
+    })
+}
+
+func Fuzz_error(f *testing.F) {
+    add(f)
+    f.Fuzz(func(t *testing.T, b []byte) {
+        if len(b) == 3 {
+            t.Error("error here")
+        }
+    })
+}
+
+func Fuzz_fatal(f *testing.F) {
+    add(f)
+    f.Fuzz(func(t *testing.T, b []byte) {
+        if len(b) == 0 {
+            t.Fatal("fatal here")
+        }
+    })
+}
+
+func Fuzz_panic(f *testing.F) {
+    add(f)
+    f.Fuzz(func(t *testing.T, b []byte) {
+        if len(b) == 5 {
+            panic("off by one error")
+        }
+    })
+}
+
+func Fuzz_nilPanic(f *testing.F) {
+    add(f)
+    f.Fuzz(func(t *testing.T, b []byte) {
+        if len(b) == 3 {
+            panic(nil)
+        }
+    })
+}
+
+func Fuzz_unsupported(f *testing.F) {
+    m := make(map[string]bool)
+    f.Add(m)
+    f.Fuzz(func(*testing.T, []byte) {})
+}
+
+func Fuzz_addDifferentNumber(f *testing.F) {
+    f.Add([]byte("a"))
+    f.Add([]byte("a"), []byte("b"))
+    f.Fuzz(func(*testing.T, []byte) {})
+}
+
+func Fuzz_addDifferentType(f *testing.F) {
+    f.Add(false)
+    f.Add(1234)
+    f.Fuzz(func(*testing.T, []byte) {})
+}
+
+func Fuzz_wrongType(f *testing.F) {
+    f.Add("hello", 50)
+    f.Fuzz(func(*testing.T, []byte, int8) {})
+}
+
+-- corpustesting/fuzz_testdata_corpus_test.go --
+package fuzz_testdata_corpus
+
+import "testing"
+
+func fuzzFn(f *testing.F) {
+    f.Helper()
+    f.Fuzz(func(t *testing.T, b []byte) {
+        if string(b) == "12345" {
+            t.Fatal("fatal here")
+        }
+    })
+}
+
+func Fuzz_fail(f *testing.F) {
+    fuzzFn(f)
+}
+
+func Fuzz_pass(f *testing.F) {
+    fuzzFn(f)
+}
+
+func Fuzz_passString(f *testing.F) {
+    f.Add("some seed corpus")
+    f.Fuzz(func(*testing.T, string) {})
+}
+
+func Fuzz_panic(f *testing.F) {
+    f.Fuzz(func(t *testing.T, b []byte) {})
+}
+
+func Fuzz_inNestedDir(f *testing.F) {
+    f.Fuzz(func(t *testing.T, b []byte) {})
+}
+
+func Fuzz_wrongType(f *testing.F) {
+    f.Fuzz(func(t *testing.T, b []byte) {})
+}
+
+-- corpustesting/testdata/fuzz/Fuzz_fail/1 --
+go test fuzz v1
+[]byte("12345")
+-- corpustesting/testdata/fuzz/Fuzz_pass/1 --
+go test fuzz v1
+[]byte("00000")
+-- corpustesting/testdata/fuzz/Fuzz_passString/1 --
+go test fuzz v1
+string("hello")
+-- corpustesting/testdata/fuzz/Fuzz_panic/1 --
+malformed
+-- corpustesting/testdata/fuzz/Fuzz_inNestedDir/anotherdir/1 --
+go test fuzz v1
+[]byte("12345")
+-- corpustesting/testdata/fuzz/Fuzz_wrongType/1 --
+go test fuzz v1
+int("00000")
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_cache.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_cache.txt
new file mode 100644 (file)
index 0000000..752ab3a
--- /dev/null
@@ -0,0 +1,97 @@
+[!fuzz-instrumented] skip
+
+[short] skip
+env GOCACHE=$WORK/cache
+
+# Fuzz cache should not exist after a regular test run.
+go test .
+exists $GOCACHE
+! exists $GOCACHE/fuzz
+
+# Fuzzing should write interesting values to the cache.
+go test -fuzz=FuzzY -fuzztime=100x .
+go run ./contains_files $GOCACHE/fuzz/example.com/y/FuzzY
+
+# 'go clean -cache' should not delete the fuzz cache.
+go clean -cache
+exists $GOCACHE/fuzz
+
+# 'go clean -fuzzcache' should delete the fuzz cache but not the build cache.
+go build -x ./empty
+stderr '(compile|gccgo)( |\.exe).*empty.go'
+go clean -fuzzcache
+! exists $GOCACHE/fuzz
+go build -x ./empty
+! stderr '(compile|gccgo)( |\.exe).*empty.go'
+
+# Fuzzing indicates that one new interesting value was found with an empty
+# corpus, and the total size of the cache is now 1.
+go clean -fuzzcache
+go test -fuzz=FuzzEmpty -fuzztime=10000x .
+stdout 'new interesting: 1'
+stdout 'total: 1'
+
+# Fuzzing again with a small fuzztime does not find any other interesting
+# values but still indicates that the cache size is 1.
+go test -fuzz=FuzzEmpty -fuzztime=2x .
+stdout 'new interesting: 0'
+stdout 'total: 1'
+
+! go clean -fuzzcache example.com/y
+stderr 'go: clean -fuzzcache cannot be used with package arguments'
+
+-- go.mod --
+module example.com/y
+
+go 1.16
+-- y_test.go --
+package y
+
+import (
+       "io"
+       "testing"
+)
+
+func FuzzEmpty(f *testing.F) {
+    f.Fuzz(func (*testing.T, []byte) {})
+}
+
+func FuzzY(f *testing.F) {
+       f.Add([]byte("y"))
+       f.Fuzz(func(t *testing.T, b []byte) { Y(io.Discard, b) })
+}
+-- y.go --
+package y
+
+import (
+       "bytes"
+       "io"
+)
+
+func Y(w io.Writer, b []byte) {
+       if !bytes.Equal(b, []byte("y")) {
+               w.Write([]byte("not equal"))
+       }
+}
+-- empty/empty.go --
+package empty
+-- contains_files/contains_files.go --
+package main
+
+import (
+       "fmt"
+       "path/filepath"
+       "io/ioutil"
+       "os"
+)
+
+func main() {
+       infos, err := ioutil.ReadDir(filepath.Clean(os.Args[1]))
+       if err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(1)
+       }
+       if len(infos) == 0 {
+               os.Exit(1)
+       }
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_cgo.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_cgo.txt
new file mode 100644 (file)
index 0000000..1a04877
--- /dev/null
@@ -0,0 +1,28 @@
+[!fuzz] skip
+[!cgo] skip
+[short] skip
+env GOCACHE=$WORK/cache
+
+# Test that fuzzing works with cgo (issue 65169)
+
+go test -fuzz=. -fuzztime=1x
+stdout ok
+! stdout FAIL
+
+-- go.mod --
+module example.com/p
+
+go 1.20
+-- c.go --
+package p
+
+import "C"
+-- c_test.go --
+package p
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+       f.Add(0)
+       f.Fuzz(func(t *testing.T, x int) {})
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_chatty.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_chatty.txt
new file mode 100644 (file)
index 0000000..01a68cb
--- /dev/null
@@ -0,0 +1,103 @@
+[!fuzz] skip
+[short] skip
+env GOCACHE=$WORK/cache
+
+# Run chatty fuzz targets with an error.
+! go test -v chatty_error_fuzz_test.go
+! stdout '^ok'
+stdout 'FAIL'
+stdout 'error in target'
+
+# Run chatty fuzz targets with a fatal.
+! go test -v chatty_fatal_fuzz_test.go
+! stdout '^ok'
+stdout 'FAIL'
+stdout 'fatal in target'
+
+# Run chatty fuzz target with a panic
+! go test -v chatty_panic_fuzz_test.go
+! stdout ^ok
+stdout FAIL
+stdout 'this is bad'
+
+# Run skipped chatty fuzz targets.
+go test -v chatty_skipped_fuzz_test.go
+stdout ok
+stdout SKIP
+! stdout FAIL
+
+# Run successful chatty fuzz targets.
+go test -v chatty_fuzz_test.go
+stdout ok
+stdout PASS
+stdout 'all good here'
+! stdout FAIL
+
+# Fuzz successful chatty fuzz target that includes a separate unit test.
+go test -v chatty_with_test_fuzz_test.go -fuzz=Fuzz -fuzztime=1x
+stdout ok
+stdout PASS
+! stdout FAIL
+stdout -count=1 'all good here'
+# Verify that the unit test is only run once.
+stdout -count=1 'logged foo'
+
+-- chatty_error_fuzz_test.go --
+package chatty_error_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Error("error in target")
+}
+
+-- chatty_fatal_fuzz_test.go --
+package chatty_fatal_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Fatal("fatal in target")
+}
+
+-- chatty_panic_fuzz_test.go --
+package chatty_panic_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    panic("this is bad")
+}
+
+-- chatty_skipped_fuzz_test.go --
+package chatty_skipped_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Skip()
+}
+
+-- chatty_fuzz_test.go --
+package chatty_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Log("all good here")
+    f.Fuzz(func(*testing.T, []byte) {})
+}
+
+-- chatty_with_test_fuzz_test.go --
+package chatty_with_test_fuzz
+
+import "testing"
+
+func TestFoo(t *testing.T) {
+    t.Log("logged foo")
+}
+
+func Fuzz(f *testing.F) {
+    f.Log("all good here")
+    f.Fuzz(func(*testing.T, []byte) {})
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_cleanup.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_cleanup.txt
new file mode 100644 (file)
index 0000000..5f86498
--- /dev/null
@@ -0,0 +1,67 @@
+[!fuzz] skip
+[short] skip
+env GOCACHE=$WORK/cache
+
+# Cleanup should run after F.Skip.
+go test -run=FuzzTargetSkip
+stdout cleanup
+
+# Cleanup should run after F.Fatal.
+! go test -run=FuzzTargetFatal
+stdout cleanup
+
+# Cleanup should run after an unexpected runtime.Goexit.
+! go test -run=FuzzTargetGoexit
+stdout cleanup
+
+# Cleanup should run after panic.
+! go test -run=FuzzTargetPanic
+stdout cleanup
+
+# Cleanup should run in fuzz function on seed corpus.
+go test -v -run=FuzzFunction
+stdout '(?s)inner.*outer'
+
+# TODO(jayconrod): test cleanup while fuzzing. For now, the worker process's
+# stdout and stderr is connected to the coordinator's, but it should eventually
+# be connected to os.DevNull, so we wouldn't see t.Log output.
+
+-- go.mod --
+module cleanup
+
+go 1.15
+-- cleanup_test.go --
+package cleanup
+
+import (
+       "runtime"
+       "testing"
+)
+
+func FuzzTargetSkip(f *testing.F) {
+       f.Cleanup(func() { f.Log("cleanup") })
+       f.Skip()
+}
+
+func FuzzTargetFatal(f *testing.F) {
+       f.Cleanup(func() { f.Log("cleanup") })
+       f.Fatal()
+}
+
+func FuzzTargetGoexit(f *testing.F) {
+       f.Cleanup(func() { f.Log("cleanup") })
+       runtime.Goexit()
+}
+
+func FuzzTargetPanic(f *testing.F) {
+       f.Cleanup(func() { f.Log("cleanup") })
+       panic("oh no")
+}
+
+func FuzzFunction(f *testing.F) {
+       f.Add([]byte{0})
+       f.Cleanup(func() { f.Log("outer") })
+       f.Fuzz(func(t *testing.T, b []byte) {
+               t.Cleanup(func() { t.Logf("inner") })
+       })
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_context.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_context.txt
new file mode 100644 (file)
index 0000000..a830684
--- /dev/null
@@ -0,0 +1,47 @@
+[!fuzz] skip
+[short] skip
+env GOCACHE=$WORK/cache
+
+# Test fuzz.Context.
+go test -vet=off context_fuzz_test.go
+stdout ^ok
+! stdout FAIL
+
+go test -vet=off -fuzz=Fuzz -fuzztime=1x context_fuzz_test.go
+stdout ok
+! stdout FAIL
+
+-- context_fuzz_test.go --
+package context_fuzz
+
+import (
+       "context"
+       "errors"
+       "testing"
+)
+
+func Fuzz(f *testing.F) {
+       ctx := f.Context()
+       if err := ctx.Err(); err != nil {
+               f.Fatalf("expected non-canceled context, got %v", err)
+       }
+
+       f.Fuzz(func(t *testing.T, data []byte) {
+               innerCtx := t.Context()
+               if err := innerCtx.Err(); err != nil {
+                       t.Fatalf("expected inner test to not inherit canceled context, got %v", err)
+               }
+
+               t.Cleanup(func() {
+                       if !errors.Is(innerCtx.Err(), context.Canceled) {
+                               t.Fatal("expected context of inner test to be canceled after its fuzz function finished")
+                       }
+               })
+       })
+
+       f.Cleanup(func() {
+               if !errors.Is(ctx.Err(), context.Canceled) {
+                       f.Fatal("expected context canceled before cleanup")
+               }
+       })
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_cov.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_cov.txt
new file mode 100644 (file)
index 0000000..c0844a3
--- /dev/null
@@ -0,0 +1,36 @@
+# Test that coverage instrumentation is working. Without the instrumentation
+# it is _extremely_ unlikely that the fuzzer would produce this particular
+# input in any reasonable amount of time.
+
+[short] skip
+[!fuzz-instrumented] skip
+env GOCACHE=$WORK/cache
+
+# TODO(#51484): enabled debugging info to help diagnose a deadlock in the fuzzer
+env GODEBUG=fuzzdebug=1
+! go test -fuzz=FuzzCov -v
+! stderr 'cov instrumentation working'
+
+-- go.mod --
+module test
+
+-- cov_test.go --
+package cov
+
+import "testing"
+
+func FuzzCov(f *testing.F) {
+       f.Fuzz(func(t *testing.T, b []byte) {
+               if len(b) == 8 &&
+                       b[0] == 'h' &&
+                       b[1] == 'e' &&
+                       b[2] == 'l' &&
+                       b[3] == 'l' &&
+                       b[4] == 'o' &&
+                       b[5] == ' ' &&
+                       b[6] == ':' &&
+                       b[7] == ')' {
+                       panic("cov instrumentation working")
+               }
+       })
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_deadline.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_deadline.txt
new file mode 100644 (file)
index 0000000..a51df34
--- /dev/null
@@ -0,0 +1,46 @@
+[!fuzz] skip
+[short] skip
+env GOCACHE=$WORK/cache
+
+# Warm up the build cache with GOMAXPROCS unrestricted.
+go test -c -o $devnull
+
+# For the fuzzing phase, we reduce GOMAXPROCS to avoid consuming too many
+# resources during the test. Ideally this would just free up resources to run
+# other parallel tests more quickly, but unfortunately it is actually necessary
+# in some 32-bit environments to prevent the fuzzing engine from running out of
+# address space (see https://go.dev/issue/65434).
+env GOMAXPROCS=2
+
+# The fuzz function should be able to detect whether -timeout
+# was set with T.Deadline. Note there is no F.Deadline, and
+# there is no timeout while fuzzing, even if -fuzztime is set.
+go test -run=FuzzDeadline -wantdeadline=true # -timeout defaults to 10m
+go test -run=FuzzDeadline -timeout=0 -wantdeadline=false
+! go test -run=FuzzDeadline -timeout=1s -wantdeadline=false
+go test -run=FuzzDeadline -timeout=1s -wantdeadline=true
+go test -fuzz=FuzzDeadline -timeout=0 -fuzztime=1s -wantdeadline=false
+go test -fuzz=FuzzDeadline -timeout=0 -fuzztime=100x -wantdeadline=false
+
+-- go.mod --
+module fuzz
+
+go 1.16
+-- fuzz_deadline_test.go --
+package fuzz_test
+
+import (
+       "flag"
+       "testing"
+)
+
+var wantDeadline = flag.Bool("wantdeadline", false, "whether the test should have a deadline")
+
+func FuzzDeadline(f *testing.F) {
+       f.Add("run once")
+       f.Fuzz(func (t *testing.T, _ string) {
+               if _, hasDeadline := t.Deadline(); hasDeadline != *wantDeadline {
+                       t.Fatalf("function got %v; want %v", hasDeadline, *wantDeadline)
+               }
+       })
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_dup_cache.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_dup_cache.txt
new file mode 100644 (file)
index 0000000..f54a77c
--- /dev/null
@@ -0,0 +1,53 @@
+[!fuzz] skip
+[short] skip
+env GOCACHE=$WORK/cache
+
+# This test checks that cached corpus loading properly handles duplicate entries (this can
+# happen when a f.Add value has a duplicate entry in the cached corpus.) Duplicate entries
+# should be discarded, and the rest of the cache should be loaded as normal.
+
+env GOCACHE=$WORK/cache
+env GODEBUG=fuzzdebug=1
+
+mkdir -p $GOCACHE/fuzz/fuzztest/FuzzTarget
+go run ./populate $GOCACHE/fuzz/fuzztest/FuzzTarget
+
+go test -fuzz=FuzzTarget -fuzztime=10x .
+stdout 'entries: 5'
+
+-- go.mod --
+module fuzztest
+
+go 1.17
+
+-- fuzz_test.go --
+package fuzz
+
+import "testing"
+
+func FuzzTarget(f *testing.F) {
+    f.Add(int(0))
+    f.Fuzz(func(t *testing.T, _ int) {})
+}
+
+-- populate/main.go --
+package main
+
+import (
+    "path/filepath"
+       "fmt"
+       "os"
+)
+
+func main() {
+       for i := 0; i < 10; i++ {
+               b := byte(0)
+               if i > 5 {
+                       b = byte(i)
+               }
+        tmpl := "go test fuzz v1\nint(%d)\n"
+               if err := os.WriteFile(filepath.Join(os.Args[1], fmt.Sprint(i)), []byte(fmt.Sprintf(tmpl, b)), 0777); err != nil {
+                       panic(err)
+               }
+       }
+}
\ No newline at end of file
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_err_deadlock.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_err_deadlock.txt
new file mode 100644 (file)
index 0000000..4feb41a
--- /dev/null
@@ -0,0 +1,50 @@
+[short] skip
+[!fuzz-instrumented] skip
+
+env GOCACHE=$WORK/cache
+! go test -fuzz=FuzzDead -v
+# This is a somewhat inexact check, but since we don't prefix the error with anything
+# and as the error suffix is platform dependent, this is the best we can do. In the
+# deadlock failure case, the test will just deadlock and timeout anyway, so it should
+# be clear that that failure mode is different.
+stdout 'open'
+
+-- go.mod --
+module test
+
+-- cov_test.go --
+package dead
+
+import (
+       "os"
+       "path/filepath"
+       "testing"
+       "time"
+)
+
+func FuzzDead(f *testing.F) {
+       go func() {
+               c := filepath.Join(os.Getenv("GOCACHE"), "fuzz", "test", "FuzzDead")
+               t := time.NewTicker(time.Second)
+               for range t.C {
+                       files, _ := os.ReadDir(c)
+                       if len(files) > 0 {
+                               os.RemoveAll(c)
+                       }
+               }
+       }()
+
+       f.Fuzz(func(t *testing.T, b []byte) {
+               if len(b) == 8 &&
+                       b[0] == 'h' &&
+                       b[1] == 'e' &&
+                       b[2] == 'l' &&
+                       b[3] == 'l' &&
+                       b[4] == 'o' &&
+                       b[5] == ' ' &&
+                       b[6] == ':' &&
+                       b[7] == ')' {
+                       return
+               }
+       })
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_fuzztime.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_fuzztime.txt
new file mode 100644 (file)
index 0000000..3cc2398
--- /dev/null
@@ -0,0 +1,129 @@
+skip # a 5s timeout is never going to be reliable (go.dev/issue/72140)
+
+[!fuzz] skip
+[short] skip
+env GOCACHE=$WORK/cache
+
+# There are no seed values, so 'go test' should finish quickly.
+go test
+
+# For the fuzzing phase, we reduce GOMAXPROCS to avoid consuming too many
+# resources during the test. Ideally this would just free up resources to run
+# other parallel tests more quickly, but unfortunately it is actually necessary
+# in some 32-bit environments to prevent the fuzzing engine from running out of
+# address space (see https://go.dev/issue/65434).
+env GOMAXPROCS=2
+
+# Fuzzing should exit 0 after fuzztime, even if timeout is short.
+go test -timeout=3s -fuzz=FuzzFast -fuzztime=5s
+
+# We should see the same behavior when invoking the test binary directly.
+go test -c
+exec ./fuzz.test$GOEXE -test.timeout=3s -test.fuzz=FuzzFast -test.fuzztime=5s -test.parallel=1 -test.fuzzcachedir=$WORK/cache
+
+# Timeout should not cause inputs to be written as crashers.
+! exists testdata/fuzz
+
+# When we use fuzztime with an "x" suffix, it runs a specific number of times.
+# This fuzz function creates a file with a unique name ($pid.$count) on each
+# run. We count the files to find the number of runs.
+mkdir count
+go test -fuzz=FuzzTestCount -fuzztime=1000x -fuzzminimizetime=1x
+go run check_file_count.go count 1000
+
+# When we use fuzzminimizetime with an "x" suffix, it runs a specific number of
+# times while minimizing. This fuzz function creates a file with a unique name
+# ($pid.$count) on each run once the first crash has been found. That means that
+# there should be one file for each execution of the fuzz function during
+# minimization, so we count these to determine how many times minimization was
+# run.
+mkdir minimizecount
+! go test -fuzz=FuzzMinimizeCount -fuzzminimizetime=3x -parallel=1
+go run check_file_count.go minimizecount 3
+
+-- go.mod --
+module fuzz
+
+go 1.16
+-- fuzz_fast_test.go --
+package fuzz_test
+
+import "testing"
+
+func FuzzFast(f *testing.F) {
+       f.Fuzz(func (*testing.T, []byte) {})
+}
+-- fuzz_count_test.go --
+package fuzz
+
+import (
+       "fmt"
+       "os"
+       "testing"
+)
+
+func FuzzTestCount(f *testing.F) {
+       pid := os.Getpid()
+       n := 0
+       f.Fuzz(func(t *testing.T, _ []byte) {
+               name := fmt.Sprintf("count/%v.%d", pid, n)
+               if err := os.WriteFile(name, nil, 0666); err != nil {
+                       t.Fatal(err)
+               }
+               n++
+       })
+}
+-- fuzz_minimize_count_test.go --
+package fuzz
+
+import (
+       "bytes"
+       "fmt"
+       "os"
+       "testing"
+)
+
+func FuzzMinimizeCount(f *testing.F) {
+       pid := os.Getpid()
+       n := 0
+       seed := bytes.Repeat([]byte("a"), 357)
+       f.Add(seed)
+       crashFound := false
+       f.Fuzz(func(t *testing.T, b []byte) {
+               if crashFound {
+                       name := fmt.Sprintf("minimizecount/%v.%d", pid, n)
+                       if err := os.WriteFile(name, nil, 0666); err != nil {
+                               t.Fatal(err)
+                       }
+                       n++
+               }
+               if !bytes.Equal(b, seed) {  // this should happen right away
+                       crashFound = true
+                       t.Error("minimize this!")
+               }
+       })
+}
+-- check_file_count.go --
+// +build ignore
+
+package main
+
+import (
+       "fmt"
+       "os"
+       "strconv"
+)
+
+func main() {
+       dir, err := os.ReadDir(os.Args[1])
+       if err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(1)
+       }
+       got := len(dir)
+       want, _ := strconv.Atoi(os.Args[2])
+       if got != want {
+               fmt.Fprintf(os.Stderr, "got %d files; want %d\n", got, want)
+               os.Exit(1)
+       }
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_io_error.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_io_error.txt
new file mode 100644 (file)
index 0000000..01b4da6
--- /dev/null
@@ -0,0 +1,102 @@
+# Test that when the coordinator experiences an I/O error communicating
+# with a worker, the coordinator stops the worker and reports the error.
+# The coordinator should not record a crasher.
+#
+# We simulate an I/O error in the test by writing garbage to fuzz_out.
+# This is unlikely, but possible. It's difficult to simulate interruptions
+# due to ^C and EOF errors which are more common. We don't report those.
+[short] skip
+[!fuzz] skip
+env GOCACHE=$WORK/cache
+
+# If the I/O error occurs before F.Fuzz is called, the coordinator should
+# stop the worker and say that.
+! go test -fuzz=FuzzClosePipeBefore -parallel=1
+stdout '\s*fuzzing process terminated without fuzzing:'
+! stdout 'communicating with fuzzing process'
+! exists testdata
+
+# If the I/O error occurs after F.Fuzz is called (unlikely), just exit.
+# It's hard to distinguish this case from the worker being interrupted by ^C
+# or exiting with status 0 (which it should do when interrupted by ^C).
+! go test -fuzz=FuzzClosePipeAfter -parallel=1
+stdout '^\s*communicating with fuzzing process: invalid character ''!'' looking for beginning of value$'
+! exists testdata
+
+-- go.mod --
+module test
+
+go 1.17
+-- io_error_test.go --
+package io_error
+
+import (
+       "flag"
+       "testing"
+       "time"
+)
+
+func isWorker() bool {
+       f := flag.Lookup("test.fuzzworker")
+       if f == nil {
+               return false
+       }
+       get, ok := f.Value.(flag.Getter)
+       if !ok {
+               return false
+       }
+       return get.Get() == interface{}(true)
+}
+
+func FuzzClosePipeBefore(f *testing.F) {
+       if isWorker() {
+               sendGarbageToCoordinator(f)
+               time.Sleep(3600 * time.Second) // pause until coordinator terminates the process
+       }
+       f.Fuzz(func(*testing.T, []byte) {})
+}
+
+func FuzzClosePipeAfter(f *testing.F) {
+       f.Fuzz(func(t *testing.T, _ []byte) {
+               if isWorker() {
+                       sendGarbageToCoordinator(t)
+                       time.Sleep(3600 * time.Second) // pause until coordinator terminates the process
+               }
+       })
+}
+-- io_error_windows_test.go --
+package io_error
+
+import (
+       "fmt"
+       "os"
+       "testing"
+)
+
+func sendGarbageToCoordinator(tb testing.TB) {
+       v := os.Getenv("GO_TEST_FUZZ_WORKER_HANDLES")
+       var fuzzInFD, fuzzOutFD uintptr
+       if _, err := fmt.Sscanf(v, "%x,%x", &fuzzInFD, &fuzzOutFD); err != nil {
+               tb.Fatalf("parsing GO_TEST_FUZZ_WORKER_HANDLES: %v", err)
+       }
+       f := os.NewFile(fuzzOutFD, "fuzz_out")
+       if _, err := f.Write([]byte("!!")); err != nil {
+               tb.Fatalf("writing fuzz_out: %v", err)
+       }
+}
+-- io_error_notwindows_test.go --
+// +build !windows
+
+package io_error
+
+import (
+       "os"
+       "testing"
+)
+
+func sendGarbageToCoordinator(tb testing.TB) {
+       f := os.NewFile(4, "fuzz_out")
+       if _, err := f.Write([]byte("!!")); err != nil {
+               tb.Fatalf("writing fuzz_out: %v", err)
+       }
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_limit_dup_entry.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_limit_dup_entry.txt
new file mode 100644 (file)
index 0000000..d69f6e0
--- /dev/null
@@ -0,0 +1,37 @@
+[!fuzz] skip
+[short] skip
+env GOCACHE=$WORK/cache
+
+# FuzzA attempts to cause the mutator to create duplicate inputs that generate
+# new coverage. Previously this would trigger a corner case when the fuzzer
+# had an execution limit, causing it to deadlock and sit in the coordinator
+# loop indefinitely, failing to exit once the limit had been exhausted.
+
+go test -fuzz=FuzzA -fuzztime=100x -parallel=1
+
+-- go.mod --
+module m
+
+go 1.16
+-- fuzz_test.go --
+package fuzz_test
+
+import (
+       "fmt"
+       "testing"
+)
+
+func FuzzA(f *testing.F) {
+       f.Add([]byte("seed"))
+       i := 0
+       f.Fuzz(func(t *testing.T, b []byte) {
+               i++
+               if string(b) == "seed" {
+                       if i == 0 {
+                               fmt.Println("a")
+                       } else if i > 1 {
+                               fmt.Println("b")
+                       }
+               }
+       })
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_match.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_match.txt
new file mode 100644 (file)
index 0000000..d149586
--- /dev/null
@@ -0,0 +1,40 @@
+[!fuzz] skip
+[short] skip
+env GOCACHE=$WORK/cache
+
+# Matches only fuzz targets to test.
+go test standalone_fuzz_test.go
+! stdout '^ok.*\[no tests to run\]'
+stdout '^ok'
+
+# Matches only for fuzzing.
+go test -fuzz Fuzz -fuzztime 1x standalone_fuzz_test.go
+! stdout '^ok.*\[no tests to run\]'
+stdout '^ok'
+
+# Matches none for fuzzing but will run the fuzz target as a test.
+go test -fuzz ThisWillNotMatch -fuzztime 1x standalone_fuzz_test.go
+! stdout '^ok.*no tests to run'
+stdout '^ok'
+stdout 'no fuzz tests to fuzz'
+
+[short] stop
+
+# Matches only fuzz targets to test with -run.
+go test -run Fuzz standalone_fuzz_test.go
+! stdout '^ok.*\[no tests to run\]'
+stdout '^ok'
+
+# Matches no fuzz targets.
+go test -run ThisWillNotMatch standalone_fuzz_test.go
+stdout '^ok.*no tests to run'
+! stdout 'no fuzz tests to fuzz'
+
+-- standalone_fuzz_test.go --
+package standalone_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+       f.Fuzz(func (*testing.T, []byte) {})
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_minimize.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_minimize.txt
new file mode 100644 (file)
index 0000000..a6dc3f1
--- /dev/null
@@ -0,0 +1,203 @@
+[!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)
+       }
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_minimize_dirty_cov.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_minimize_dirty_cov.txt
new file mode 100644 (file)
index 0000000..c8af9be
--- /dev/null
@@ -0,0 +1,85 @@
+# Test that minimization doesn't use dirty coverage snapshots when it
+# is unable to actually minimize the input. We do this by checking that
+# an expected value appears in the cache. If a dirty coverage map is used
+# (i.e. the coverage map generated during the last minimization step,
+# rather than the map provided with the initial input) then this value
+# is unlikely to appear in the cache, since the map generated during
+# the last minimization step should not increase the coverage.
+
+[short] skip
+[!fuzz-instrumented] skip
+
+env GOCACHE=$WORK/gocache
+go test -fuzz=FuzzCovMin -fuzztime=500000x -test.fuzzcachedir=$GOCACHE/fuzz
+go run check_file/main.go $GOCACHE/fuzz/FuzzCovMin ab
+
+-- go.mod --
+module test
+
+-- covmin_test.go --
+package covmin
+
+import "testing"
+
+func FuzzCovMin(f *testing.F) {
+       f.Add([]byte("aa"))
+       f.Fuzz(func(t *testing.T, data []byte) {
+               if len(data) == 2 && data[0] == 'a' && data[1] == 'b' {
+                       return
+               }
+       })
+}
+
+-- check_file/main.go --
+package main
+
+import (
+       "bytes"
+       "fmt"
+       "os"
+       "path/filepath"
+       "regexp"
+       "strconv"
+)
+
+func checkFile(name, expected string) (bool, error) {
+       data, err := os.ReadFile(name)
+       if err != nil {
+               return false, err
+       }
+       for _, line := range bytes.Split(data, []byte("\n")) {
+               m := valRe.FindSubmatch(line)
+               if m == nil {
+                       continue
+               }
+               fmt.Println(strconv.Unquote(string(m[1])))
+               if s, err := strconv.Unquote(string(m[1])); err != nil {
+                       return false, err
+               } else if s == expected {
+                       return true, nil
+               }
+       }
+       return false, nil
+}
+
+var valRe = regexp.MustCompile(`^\[\]byte\(([^)]+)\)$`)
+
+func main() {
+       dir, expected := os.Args[1], os.Args[2]
+       ents, err := os.ReadDir(dir)
+       if err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(1)
+       }
+       for _, ent := range ents {
+               name := filepath.Join(dir, ent.Name())
+               if good, err := checkFile(name, expected); err != nil {
+                       fmt.Fprintln(os.Stderr, err)
+                       os.Exit(1)
+               } else if good {
+                       os.Exit(0)
+               }
+       }
+       fmt.Fprintln(os.Stderr, "input over minimized")
+       os.Exit(1)
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_minimize_interesting.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_minimize_interesting.txt
new file mode 100644 (file)
index 0000000..11aaaca
--- /dev/null
@@ -0,0 +1,230 @@
+[short] skip
+[!fuzz-instrumented] skip
+
+# Test that when an interesting value is discovered (one that expands coverage),
+# the fuzzing engine minimizes it before writing it to the cache.
+#
+# The program below starts with a seed value of length 100, but more coverage
+# will be found for any value other than the seed. We should end with a value
+# in the cache of length 1 (the minimizer currently does not produce empty
+# strings). check_cache.go confirms that.
+#
+# We would like to verify that ALL values in the cache were minimized to a
+# length of 1, but this isn't always possible when new coverage is found in
+# functions called by testing or internal/fuzz in the background.
+
+go test -c -fuzz=.  # Build using shared build cache for speed.
+env GOCACHE=$WORK/gocache
+exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinCache -test.fuzztime=1000x
+go run check_cache/check_cache.go $GOCACHE/fuzz/FuzzMinCache
+
+# Test that minimization occurs for a crash that appears while minimizing a
+# newly found interesting input. There must be only one worker for this test to
+# be flaky like we want.
+! exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinimizerCrashInMinimization -test.run=^$ -test.fuzztime=10000x -test.parallel=1
+! stdout '^ok'
+stdout -count=1 'got the minimum size!'
+stdout -count=1 'bad input'
+stdout FAIL
+# Check that the input written to testdata will reproduce the error, and is the
+# smallest possible.
+go run check_testdata/check_testdata.go FuzzMinimizerCrashInMinimization 1
+
+# Test that a nonrecoverable error that occurs while minimizing an interesting
+# input is reported correctly.
+! exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinimizerNonrecoverableCrashInMinimization -test.run=^$ -test.fuzztime=10000x -test.parallel=1
+! stdout '^ok'
+stdout -count=1 'fuzzing process hung or terminated unexpectedly while minimizing'
+stdout -count=1 'EOF'
+stdout FAIL
+# Check that the input written to testdata will reproduce the error.
+go run check_testdata/check_testdata.go FuzzMinimizerNonrecoverableCrashInMinimization 1
+
+-- go.mod --
+module fuzz
+
+go 1.17
+-- y.go --
+package fuzz
+
+import (
+       "bytes"
+       "io"
+)
+
+func Y(w io.Writer, s string) {
+       if !bytes.Equal([]byte(s), []byte("y")) {
+               w.Write([]byte("not equal"))
+       }
+}
+-- fuzz_test.go --
+package fuzz
+
+import (
+       "bytes"
+       "os"
+       "testing"
+)
+
+func FuzzMinimizerCrashInMinimization(f *testing.F) {
+       seed := bytes.Repeat([]byte{255}, 100)
+       f.Add(seed)
+       f.Fuzz(func(t *testing.T, b []byte) {
+               if bytes.Equal(seed, b) {
+                       return
+               }
+               t.Error("bad input")
+               if len(b) == 1 {
+                       t.Error("got the minimum size!")
+               }
+       })
+}
+
+var fuzzing bool
+
+func FuzzMinimizerNonrecoverableCrashInMinimization(f *testing.F) {
+       seed := bytes.Repeat([]byte{255}, 100)
+       f.Add(seed)
+       f.Fuzz(func(t *testing.T, b []byte) {
+               if bytes.Equal(seed, b) {
+                       return
+               } else if len(b) == 1 {
+                       os.Exit(1)
+               }
+       })
+}
+
+func FuzzMinCache(f *testing.F) {
+       seed := bytes.Repeat([]byte("a"), 20)
+       f.Add(seed)
+       f.Fuzz(func(t *testing.T, buf []byte) {
+               if bytes.Equal(buf, seed) {
+                       return
+               }
+       })
+}
+-- check_testdata/check_testdata.go --
+//go:build ignore
+// +build ignore
+
+// check_testdata.go checks that the string written
+// is not longer than the provided length.
+package main
+
+import (
+       "bytes"
+       "fmt"
+       "io/ioutil"
+       "os"
+       "path/filepath"
+       "regexp"
+       "strconv"
+)
+
+func main() {
+       wantLen, err := strconv.Atoi(os.Args[2])
+       if err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(1)
+       }
+       testName := os.Args[1]
+       dir := filepath.Join("testdata/fuzz", testName)
+
+       files, err := ioutil.ReadDir(dir)
+       if err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(1)
+       }
+
+       if len(files) == 0 {
+               fmt.Fprintf(os.Stderr, "expect at least one failure to be written to testdata\n")
+               os.Exit(1)
+       }
+
+       for _, f := range files {
+               data, err := ioutil.ReadFile(filepath.Join(dir, f.Name()))
+               if err != nil {
+                       panic(err)
+               }
+               var containsVal bool
+               for _, line := range bytes.Split(data, []byte("\n")) {
+                       m := valRe.FindSubmatch(line)
+                       if m == nil {
+                               continue
+                       }
+                       containsVal = true
+                       s, err := strconv.Unquote(string(m[1]))
+                       if err != nil {
+                               panic(err)
+                       }
+                       if len(s) != wantLen {
+                               fmt.Fprintf(os.Stderr, "expect length %d, got %d (%q)\n", wantLen, len(s), line)
+                               os.Exit(1)
+                       }
+               }
+               if !containsVal {
+                       fmt.Fprintln(os.Stderr, "corpus file contained no values")
+                       os.Exit(1)
+               }
+       }
+}
+
+var valRe = regexp.MustCompile(`^\[\]byte\(([^)]+)\)$`)
+
+-- check_cache/check_cache.go --
+//go:build ignore
+// +build ignore
+
+// check_cache.go checks that each file in the cached corpus has a []byte
+// of length at most 1. This verifies that at least one cached input is minimized.
+package main
+
+import (
+       "bytes"
+       "fmt"
+       "os"
+       "path/filepath"
+       "regexp"
+       "strconv"
+)
+
+func main() {
+       dir := os.Args[1]
+       ents, err := os.ReadDir(dir)
+       if err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(1)
+       }
+       for _, ent := range ents {
+               name := filepath.Join(dir, ent.Name())
+               if good, err := checkCacheFile(name); err != nil {
+                       fmt.Fprintln(os.Stderr, err)
+                       os.Exit(1)
+               } else if good {
+                       os.Exit(0)
+               }
+       }
+       fmt.Fprintln(os.Stderr, "no cached inputs were minimized")
+       os.Exit(1)
+}
+
+func checkCacheFile(name string) (good bool, err error) {
+       data, err := os.ReadFile(name)
+       if err != nil {
+               return false, err
+       }
+       for _, line := range bytes.Split(data, []byte("\n")) {
+               m := valRe.FindSubmatch(line)
+               if m == nil {
+                       continue
+               }
+               if s, err := strconv.Unquote(string(m[1])); err != nil {
+                       return false, err
+               } else if len(s) <= 1 {
+                       return true, nil
+               }
+       }
+       return false, nil
+}
+
+var valRe = regexp.MustCompile(`^\[\]byte\(([^)]+)\)$`)
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_multiple.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_multiple.txt
new file mode 100644 (file)
index 0000000..c96112f
--- /dev/null
@@ -0,0 +1,50 @@
+# This test checks that 'go test' prints a reasonable error when fuzzing is
+# enabled, and multiple package or multiple fuzz targets match.
+# TODO(#46312): support fuzzing multiple targets in multiple packages.
+
+[!fuzz] skip
+[short] skip
+env GOCACHE=$WORK/cache
+
+# With fuzzing disabled, multiple targets can be tested.
+go test ./...
+
+# With fuzzing enabled, at most one package may be tested,
+# even if only one package contains fuzz targets.
+! go test -fuzz=. ./...
+stderr '^cannot use -fuzz flag with multiple packages$'
+! go test -fuzz=. ./zero ./one
+stderr '^cannot use -fuzz flag with multiple packages$'
+go test -fuzz=. -fuzztime=1x ./one
+
+# With fuzzing enabled, at most one target in the same package may match.
+! go test -fuzz=. ./two
+stdout '^testing: will not fuzz, -fuzz matches more than one fuzz test: \[FuzzOne FuzzTwo\]$'
+go test -fuzz=FuzzTwo -fuzztime=1x ./two
+
+-- go.mod --
+module fuzz
+
+go 1.18
+-- zero/zero.go --
+package zero
+-- one/one_test.go --
+package one
+
+import "testing"
+
+func FuzzOne(f *testing.F) {
+  f.Fuzz(func(*testing.T, []byte) {})
+}
+-- two/two_test.go --
+package two
+
+import "testing"
+
+func FuzzOne(f *testing.F) {
+  f.Fuzz(func(*testing.T, []byte) {})
+}
+
+func FuzzTwo(f *testing.F) {
+  f.Fuzz(func(*testing.T, []byte) {})
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_mutate_crash.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_mutate_crash.txt
new file mode 100644 (file)
index 0000000..4b9b36d
--- /dev/null
@@ -0,0 +1,323 @@
+[!fuzz] skip
+
+# Tests that a crash caused by a mutator-discovered input writes the bad input
+# to testdata, and fails+reports correctly. This tests the end-to-end behavior
+# of the mutator finding a crash while fuzzing, adding it as a regression test
+# to the seed corpus in testdata, and failing the next time the test is run.
+
+[short] skip
+env GOCACHE=$WORK/cache
+
+# Running the seed corpus for all of the targets should pass the first
+# time, since nothing in the seed corpus will cause a crash.
+go test
+
+# Running the fuzzer should find a crashing input quickly.
+! go test -fuzz=FuzzWithBug -fuzztime=100x -fuzzminimizetime=1000x
+stdout 'testdata[/\\]fuzz[/\\]FuzzWithBug[/\\]'
+stdout 'this input caused a crash!'
+go run check_testdata.go FuzzWithBug
+
+# Now, the failing bytes should have been added to the seed corpus for
+# the target, and should fail when run without fuzzing.
+! go test
+stdout 'FuzzWithBug/[a-f0-9]{16}'
+stdout 'this input caused a crash!'
+
+! go test -run=FuzzWithNilPanic -fuzz=FuzzWithNilPanic -fuzztime=100x -fuzzminimizetime=1000x
+stdout 'testdata[/\\]fuzz[/\\]FuzzWithNilPanic[/\\]'
+stdout 'panic called with nil argument|test executed panic.nil. or runtime.Goexit'
+go run check_testdata.go FuzzWithNilPanic
+
+! go test -run=FuzzWithGoexit -fuzz=FuzzWithGoexit -fuzztime=100x -fuzzminimizetime=1000x
+stdout 'testdata[/\\]fuzz[/\\]FuzzWithGoexit[/\\]'
+stdout 'runtime.Goexit'
+go run check_testdata.go FuzzWithGoexit
+
+! go test -run=FuzzWithFail -fuzz=FuzzWithFail -fuzztime=100x -fuzzminimizetime=1000x
+stdout 'testdata[/\\]fuzz[/\\]FuzzWithFail[/\\]'
+go run check_testdata.go FuzzWithFail
+
+! go test -run=FuzzWithLogFail -fuzz=FuzzWithLogFail -fuzztime=100x -fuzzminimizetime=1000x
+stdout 'testdata[/\\]fuzz[/\\]FuzzWithLogFail[/\\]'
+stdout 'logged something'
+go run check_testdata.go FuzzWithLogFail
+
+! go test -run=FuzzWithErrorf -fuzz=FuzzWithErrorf -fuzztime=100x -fuzzminimizetime=1000x
+stdout 'testdata[/\\]fuzz[/\\]FuzzWithErrorf[/\\]'
+stdout 'errorf was called here'
+go run check_testdata.go FuzzWithErrorf
+
+! go test -run=FuzzWithFatalf -fuzz=FuzzWithFatalf -fuzztime=100x -fuzzminimizetime=1000x
+stdout 'testdata[/\\]fuzz[/\\]FuzzWithFatalf[/\\]'
+stdout 'fatalf was called here'
+go run check_testdata.go FuzzWithFatalf
+
+! go test -run=FuzzWithBadExit -fuzz=FuzzWithBadExit -fuzztime=100x -fuzzminimizetime=1000x
+stdout 'testdata[/\\]fuzz[/\\]FuzzWithBadExit[/\\]'
+stdout '^\s+fuzzing process hung or terminated unexpectedly: exit status'
+go run check_testdata.go FuzzWithBadExit
+
+! go test -run=FuzzDeadlock -fuzz=FuzzDeadlock -fuzztime=100x -fuzzminimizetime=0x
+stdout 'testdata[/\\]fuzz[/\\]FuzzDeadlock[/\\]'
+stdout '^\s+fuzzing process hung or terminated unexpectedly: exit status'
+go run check_testdata.go FuzzDeadlock
+
+# Running the fuzzer should find a crashing input quickly for fuzzing two types.
+! go test -run=FuzzWithTwoTypes -fuzz=FuzzWithTwoTypes -fuzztime=100x -fuzzminimizetime=1000x
+stdout 'testdata[/\\]fuzz[/\\]FuzzWithTwoTypes[/\\]'
+stdout 'these inputs caused a crash!'
+go run check_testdata.go FuzzWithTwoTypes
+
+# Running the fuzzer should find a crashing input quickly for an integer.
+! go test -run=FuzzInt -fuzz=FuzzInt -fuzztime=100x -fuzzminimizetime=1000x
+stdout 'testdata[/\\]fuzz[/\\]FuzzInt[/\\]'
+stdout 'this input caused a crash!'
+go run check_testdata.go FuzzInt
+
+! go test -run=FuzzUint -fuzz=FuzzUint -fuzztime=100x -fuzzminimizetime=1000x
+stdout 'testdata[/\\]fuzz[/\\]FuzzUint[/\\]'
+stdout 'this input caused a crash!'
+go run check_testdata.go FuzzUint
+
+# Running the fuzzer should find a crashing input quickly for a bool.
+! go test -run=FuzzBool -fuzz=FuzzBool -fuzztime=100x -fuzzminimizetime=1000x
+stdout 'testdata[/\\]fuzz[/\\]FuzzBool[/\\]'
+stdout 'this input caused a crash!'
+go run check_testdata.go FuzzBool
+
+# Running the fuzzer should find a crashing input quickly for a float.
+! go test -run=FuzzFloat -fuzz=FuzzFloat -fuzztime=100x -fuzzminimizetime=1000x
+stdout 'testdata[/\\]fuzz[/\\]FuzzFloat[/\\]'
+stdout 'this input caused a crash!'
+go run check_testdata.go FuzzFloat
+
+# Running the fuzzer should find a crashing input quickly for a byte.
+! go test -run=FuzzByte -fuzz=FuzzByte -fuzztime=100x -fuzzminimizetime=1000x
+stdout 'testdata[/\\]fuzz[/\\]FuzzByte[/\\]'
+stdout 'this input caused a crash!'
+go run check_testdata.go FuzzByte
+
+# Running the fuzzer should find a crashing input quickly for a rune.
+! go test -run=FuzzRune -fuzz=FuzzRune -fuzztime=100x -fuzzminimizetime=1000x
+stdout 'testdata[/\\]fuzz[/\\]FuzzRune[/\\]'
+stdout 'this input caused a crash!'
+go run check_testdata.go FuzzRune
+
+# Running the fuzzer should find a crashing input quickly for a string.
+! go test -run=FuzzString -fuzz=FuzzString -fuzztime=100x -fuzzminimizetime=1000x
+stdout 'testdata[/\\]fuzz[/\\]FuzzString[/\\]'
+stdout 'this input caused a crash!'
+go run check_testdata.go FuzzString
+
+-- go.mod --
+module m
+
+go 1.16
+-- fuzz_crash_test.go --
+package fuzz_crash
+
+import (
+       "os"
+       "runtime"
+       "testing"
+)
+
+func FuzzWithBug(f *testing.F) {
+       f.Add([]byte("aa"))
+       f.Fuzz(func(t *testing.T, b []byte) {
+               if string(b) != "aa" {
+                       panic("this input caused a crash!")
+               }
+       })
+}
+
+func FuzzWithNilPanic(f *testing.F) {
+       f.Add([]byte("aa"))
+       f.Fuzz(func(t *testing.T, b []byte) {
+               if string(b) != "aa" {
+                       panic(nil)
+               }
+       })
+}
+
+func FuzzWithGoexit(f *testing.F) {
+       f.Add([]byte("aa"))
+       f.Fuzz(func(t *testing.T, b []byte) {
+               if string(b) != "aa" {
+                       runtime.Goexit()
+               }
+       })
+}
+
+func FuzzWithFail(f *testing.F) {
+       f.Add([]byte("aa"))
+       f.Fuzz(func(t *testing.T, b []byte) {
+               if string(b) != "aa" {
+                       t.Fail()
+               }
+       })
+}
+
+func FuzzWithLogFail(f *testing.F) {
+       f.Add([]byte("aa"))
+       f.Fuzz(func(t *testing.T, b []byte) {
+               if string(b) != "aa" {
+                       t.Log("logged something")
+                       t.Fail()
+               }
+       })
+}
+
+func FuzzWithErrorf(f *testing.F) {
+       f.Add([]byte("aa"))
+       f.Fuzz(func(t *testing.T, b []byte) {
+               if string(b) != "aa" {
+                       t.Errorf("errorf was called here")
+               }
+       })
+}
+
+func FuzzWithFatalf(f *testing.F) {
+       f.Add([]byte("aa"))
+       f.Fuzz(func(t *testing.T, b []byte) {
+               if string(b) != "aa" {
+                       t.Fatalf("fatalf was called here")
+               }
+       })
+}
+
+func FuzzWithBadExit(f *testing.F) {
+       f.Add([]byte("aa"))
+       f.Fuzz(func(t *testing.T, b []byte) {
+               if string(b) != "aa" {
+                       os.Exit(1)
+               }
+       })
+}
+
+func FuzzDeadlock(f *testing.F) {
+       f.Add(int(0))
+       f.Fuzz(func(t *testing.T, n int) {
+               if n != 0 {
+                       select {}
+               }
+       })
+}
+
+func FuzzWithTwoTypes(f *testing.F) {
+       f.Fuzz(func(t *testing.T, a, b []byte) {
+               if len(a) > 0 && len(b) > 0 {
+                       panic("these inputs caused a crash!")
+               }
+       })
+}
+
+func FuzzInt(f *testing.F) {
+       f.Add(0)
+       f.Fuzz(func(t *testing.T, a int) {
+               if a != 0 {
+                       panic("this input caused a crash!")
+               }
+       })
+}
+
+func FuzzUint(f *testing.F) {
+       f.Add(uint(0))
+       f.Fuzz(func(t *testing.T, a uint) {
+               if a != 0 {
+                       panic("this input caused a crash!")
+               }
+       })
+}
+
+func FuzzBool(f *testing.F) {
+       f.Add(false)
+       f.Fuzz(func(t *testing.T, a bool) {
+               if a {
+                       panic("this input caused a crash!")
+               }
+       })
+}
+
+func FuzzFloat(f *testing.F) {
+       f.Fuzz(func(t *testing.T, a float64) {
+               if a != 0 {
+                       panic("this input caused a crash!")
+               }
+       })
+}
+
+func FuzzByte(f *testing.F) {
+       f.Add(byte(0))
+       f.Fuzz(func(t *testing.T, a byte) {
+               if a != 0 {
+                       panic("this input caused a crash!")
+               }
+       })
+}
+
+func FuzzRune(f *testing.F) {
+       f.Add(rune(0))
+       f.Fuzz(func(t *testing.T, a rune) {
+               if a != 0 {
+                       panic("this input caused a crash!")
+               }
+       })
+}
+
+func FuzzString(f *testing.F) {
+       f.Add("")
+       f.Fuzz(func(t *testing.T, a string) {
+               if a != "" {
+                       panic("this input caused a crash!")
+               }
+       })
+}
+
+-- check_testdata.go --
+// +build ignore
+
+package main
+
+import (
+       "bytes"
+       "crypto/sha256"
+       "fmt"
+       "io/ioutil"
+       "os"
+       "path/filepath"
+)
+
+func main() {
+       target := os.Args[1]
+       dir := filepath.Join("testdata/fuzz", target)
+
+       files, err := ioutil.ReadDir(dir)
+       if err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(1)
+       }
+
+       if len(files) == 0 {
+               fmt.Fprintf(os.Stderr, "expect at least one new mutation to be written to testdata\n")
+               os.Exit(1)
+       }
+
+       fname := files[0].Name()
+       contents, err := ioutil.ReadFile(filepath.Join(dir, fname))
+       if err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(1)
+       }
+       if bytes.Equal(contents, []byte("aa")) {
+               fmt.Fprintf(os.Stderr, "newly written testdata entry was not mutated\n")
+               os.Exit(1)
+       }
+       // The hash of the bytes in the file should match the filename.
+       h := []byte(fmt.Sprintf("%x", sha256.Sum256(contents)))
+       if !bytes.HasPrefix(h, []byte(fname)) {
+               fmt.Fprintf(os.Stderr, "hash of bytes %q does not match filename %q\n", h, fname)
+               os.Exit(1)
+       }
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_mutate_fail.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_mutate_fail.txt
new file mode 100644 (file)
index 0000000..213b73a
--- /dev/null
@@ -0,0 +1,103 @@
+[!fuzz] skip
+
+# Check that if a worker does not call F.Fuzz or calls F.Fail first,
+# 'go test' exits non-zero and no crasher is recorded.
+
+[short] skip
+env GOCACHE=$WORK/cache
+
+! go test -fuzz=FuzzReturn
+! exists testdata
+
+! go test -fuzz=FuzzSkip
+! exists testdata
+
+! go test -fuzz=FuzzFail
+! exists testdata
+
+! go test -fuzz=FuzzPanic
+! exists testdata
+
+! go test -fuzz=FuzzNilPanic
+! exists testdata
+
+! go test -fuzz=FuzzGoexit
+! exists testdata
+
+! go test -fuzz=FuzzExit
+! exists testdata
+
+-- go.mod --
+module m
+
+go 1.17
+-- fuzz_fail_test.go --
+package fuzz_fail
+
+import (
+       "flag"
+       "os"
+       "runtime"
+       "testing"
+)
+
+func isWorker() bool {
+       f := flag.Lookup("test.fuzzworker")
+       if f == nil {
+               return false
+       }
+       get, ok := f.Value.(flag.Getter)
+       if !ok {
+               return false
+       }
+       return get.Get() == interface{}(true)
+}
+
+func FuzzReturn(f *testing.F) {
+       if isWorker() {
+               return
+       }
+       f.Fuzz(func(*testing.T, []byte) {})
+}
+
+func FuzzSkip(f *testing.F) {
+       if isWorker() {
+               f.Skip()
+       }
+       f.Fuzz(func(*testing.T, []byte) {})
+}
+
+func FuzzFail(f *testing.F) {
+       if isWorker() {
+               f.Fail()
+       }
+       f.Fuzz(func(*testing.T, []byte) {})
+}
+
+func FuzzPanic(f *testing.F) {
+       if isWorker() {
+               panic("nope")
+       }
+       f.Fuzz(func(*testing.T, []byte) {})
+}
+
+func FuzzNilPanic(f *testing.F) {
+       if isWorker() {
+               panic(nil)
+       }
+       f.Fuzz(func(*testing.T, []byte) {})
+}
+
+func FuzzGoexit(f *testing.F) {
+       if isWorker() {
+               runtime.Goexit()
+       }
+       f.Fuzz(func(*testing.T, []byte) {})
+}
+
+func FuzzExit(f *testing.F) {
+       if isWorker() {
+               os.Exit(99)
+       }
+       f.Fuzz(func(*testing.T, []byte) {})
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_mutator.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_mutator.txt
new file mode 100644 (file)
index 0000000..cc1f989
--- /dev/null
@@ -0,0 +1,166 @@
+[!fuzz] skip
+
+# Test basic fuzzing mutator behavior.
+#
+# fuzz_test.go has two fuzz targets (FuzzA, FuzzB) which both add a seed value.
+# Each fuzz function writes the input to a log file. The coordinator and worker
+# use separate log files. check_logs.go verifies that the coordinator only
+# tests seed values and the worker tests mutated values on the fuzz target.
+
+[short] skip
+env GOCACHE=$WORK/cache
+
+go test -fuzz=FuzzA -fuzztime=100x -parallel=1 -log=fuzz
+go run check_logs.go fuzz fuzz.worker
+
+# TODO(b/181800488): remove -parallel=1, here and below. For now, when a
+# crash is found, all workers keep running, wasting resources and reducing
+# the number of executions available to the minimizer, increasing flakiness.
+
+# Test that the mutator is good enough to find several unique mutations.
+! go test -fuzz=FuzzMutator -parallel=1 -fuzztime=100x mutator_test.go
+! stdout '^ok'
+stdout FAIL
+stdout 'mutator found enough unique mutations'
+
+-- go.mod --
+module m
+
+go 1.16
+-- fuzz_test.go --
+package fuzz_test
+
+import (
+       "flag"
+       "fmt"
+       "os"
+       "testing"
+)
+
+var (
+       logPath = flag.String("log", "", "path to log file")
+       logFile *os.File
+)
+
+func TestMain(m *testing.M) {
+       flag.Parse()
+       var err error
+       logFile, err = os.OpenFile(*logPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
+       if os.IsExist(err) {
+               *logPath += ".worker"
+               logFile, err = os.OpenFile(*logPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
+       }
+       if err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(1)
+       }
+       os.Exit(m.Run())
+}
+
+func FuzzA(f *testing.F) {
+       f.Add([]byte("seed"))
+       f.Fuzz(func(t *testing.T, b []byte) {
+               fmt.Fprintf(logFile, "FuzzA %q\n", b)
+       })
+}
+
+func FuzzB(f *testing.F) {
+       f.Add([]byte("seed"))
+       f.Fuzz(func(t *testing.T, b []byte) {
+               fmt.Fprintf(logFile, "FuzzB %q\n", b)
+       })
+}
+
+-- check_logs.go --
+// +build ignore
+
+package main
+
+import (
+       "bufio"
+       "bytes"
+       "fmt"
+       "io"
+       "os"
+       "strings"
+)
+
+func main() {
+       coordPath, workerPath := os.Args[1], os.Args[2]
+
+       coordLog, err := os.Open(coordPath)
+       if err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(1)
+       }
+       defer coordLog.Close()
+       if err := checkCoordLog(coordLog); err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(1)
+       }
+
+       workerLog, err := os.Open(workerPath)
+       if err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(1)
+       }
+       defer workerLog.Close()
+       if err := checkWorkerLog(workerLog); err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(1)
+       }
+}
+
+func checkCoordLog(r io.Reader) error {
+       b, err := io.ReadAll(r)
+       if err != nil {
+               return err
+       }
+       if string(bytes.TrimSpace(b)) != `FuzzB "seed"` {
+               return fmt.Errorf("coordinator: did not test FuzzB seed")
+       }
+       return nil
+}
+
+func checkWorkerLog(r io.Reader) error {
+       scan := bufio.NewScanner(r)
+       var sawAMutant bool
+       for scan.Scan() {
+               line := scan.Text()
+               if !strings.HasPrefix(line, "FuzzA ") {
+                       return fmt.Errorf("worker: tested something other than target: %s", line)
+               }
+               if strings.TrimPrefix(line, "FuzzA ") != `"seed"` {
+                       sawAMutant = true
+               }
+       }
+       if err := scan.Err(); err != nil && err != bufio.ErrTooLong {
+               return err
+       }
+       if !sawAMutant {
+               return fmt.Errorf("worker: did not test any mutants")
+       }
+       return nil
+}
+-- mutator_test.go --
+package fuzz_test
+
+import (
+       "testing"
+)
+
+// TODO(katiehockman): re-work this test once we have a better fuzzing engine
+// (ie. more mutations, and compiler instrumentation)
+func FuzzMutator(f *testing.F) {
+       // TODO(katiehockman): simplify this once we can dedupe crashes (e.g.
+       // replace map with calls to panic, and simply count the number of crashes
+       // that were added to testdata)
+       crashes := make(map[string]bool)
+       // No seed corpus initiated
+       f.Fuzz(func(t *testing.T, b []byte) {
+               crashes[string(b)] = true
+               if len(crashes) >= 10 {
+                       panic("mutator found enough unique mutations")
+               }
+       })
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_mutator_repeat.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_mutator_repeat.txt
new file mode 100644 (file)
index 0000000..3b005c9
--- /dev/null
@@ -0,0 +1,75 @@
+# TODO(jayconrod): support shared memory on more platforms.
+[!GOOS:darwin] [!GOOS:linux] [!GOOS:windows] skip
+
+# Verify that the fuzzing engine records the actual crashing input, even when
+# a worker process terminates without communicating the crashing input back
+# to the coordinator.
+
+[short] skip
+env GOCACHE=$WORK/cache
+
+# Start fuzzing. The worker crashes after 100 iterations.
+# The fuzz function writes the crashing input to "want" before exiting.
+# The fuzzing engine reconstructs the crashing input and saves it to testdata.
+! exists want
+! go test -fuzz=. -parallel=1 -fuzztime=110x -fuzzminimizetime=10x -v
+stdout '^\s+fuzzing process hung or terminated unexpectedly: exit status'
+stdout 'Failing input written to testdata'
+
+# Run the fuzz target without fuzzing. The fuzz function is called with the
+# crashing input in testdata. The test passes if that input is identical to
+# the one saved in "want".
+exists want
+go test -want=want
+
+-- go.mod --
+module fuzz
+
+go 1.17
+-- fuzz_test.go --
+package fuzz
+
+import (
+       "bytes"
+       "flag"
+       "os"
+       "testing"
+)
+
+var wantFlag = flag.String("want", "", "file containing previous crashing input")
+
+func FuzzRepeat(f *testing.F) {
+       i := 0
+       f.Fuzz(func(t *testing.T, b []byte) {
+               i++
+               if i == 100 {
+                       f, err := os.OpenFile("want", os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
+                       if err != nil {
+                               // Couldn't create the file. Return without crashing, and try
+                               // again.
+                               i--
+                               t.Skip(err)
+                       }
+                       if _, err := f.Write(b); err != nil {
+                               // We already created the file, so if we failed to write it
+                               // there's not much we can do. The test will fail anyway, but
+                               // at least make sure the error is logged to stdout.
+                               t.Fatal(err)
+                       }
+                       if err := f.Close(); err != nil {
+                               t.Fatal(err)
+                       }
+                       os.Exit(1) // crash without communicating
+               }
+
+               if *wantFlag != "" {
+                       want, err := os.ReadFile(*wantFlag)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       if !bytes.Equal(want, b) {
+                               t.Fatalf("inputs are not equal!\n got: %q\nwant:%q", b, want)
+                       }
+               }
+       })
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_non_crash_signal.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_non_crash_signal.txt
new file mode 100644 (file)
index 0000000..94a0421
--- /dev/null
@@ -0,0 +1,76 @@
+# NOTE: this test is skipped on Windows, since there's no concept of signals.
+# When a process terminates another process, it provides an exit code.
+[GOOS:windows] skip
+[!fuzz] skip
+[short] skip
+env GOCACHE=$WORK/cache
+
+# FuzzNonCrash sends itself a signal that does not appear to be a crash.
+# We should not save a crasher.
+! go test -fuzz=FuzzNonCrash
+! exists testdata
+! stdout unreachable
+! stderr unreachable
+stdout 'fuzzing process terminated by unexpected signal; no crash will be recorded: signal: terminated'
+
+# FuzzKill sends itself a signal that cannot be caught by the worker process
+# and does not appear to be a crash.
+# We should not save a crasher.
+! go test -fuzz=FuzzKill
+! exists testdata
+! stdout unreachable
+! stderr unreachable
+stdout 'fuzzing process terminated by unexpected signal; no crash will be recorded: signal: killed'
+
+# FuzzCrash sends itself a signal that looks like a crash.
+# We should save a crasher.
+! go test -fuzz=FuzzCrash
+exists testdata/fuzz/FuzzCrash
+stdout '^\s+fuzzing process hung or terminated unexpectedly: exit status'
+
+-- go.mod --
+module test
+
+go 1.17
+-- fuzz_posix_test.go --
+// +build darwin freebsd linux
+
+package fuzz
+
+import (
+       "syscall"
+       "testing"
+)
+
+func FuzzNonCrash(f *testing.F) {
+       f.Fuzz(func(*testing.T, bool) {
+               pid := syscall.Getpid()
+               if err := syscall.Kill(pid, syscall.SIGTERM); err != nil {
+                       panic(err)
+               }
+               // signal may not be received immediately. Wait for it.
+               select{}
+       })
+}
+
+func FuzzKill(f *testing.F) {
+       f.Fuzz(func(*testing.T, bool) {
+               pid := syscall.Getpid()
+               if err := syscall.Kill(pid, syscall.SIGKILL); err != nil {
+                       panic(err)
+               }
+               // signal may not be received immediately. Wait for it.
+               select{}
+       })
+}
+
+func FuzzCrash(f *testing.F) {
+       f.Fuzz(func(*testing.T, bool) {
+               pid := syscall.Getpid()
+               if err := syscall.Kill(pid, syscall.SIGILL); err != nil {
+                       panic(err)
+               }
+               // signal may not be received immediately. Wait for it.
+               select{}
+       })
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_parallel.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_parallel.txt
new file mode 100644 (file)
index 0000000..8ff965a
--- /dev/null
@@ -0,0 +1,67 @@
+[!fuzz] skip
+[short] skip
+env GOCACHE=$WORK/cache
+
+# When running seed inputs, T.Parallel should let multiple inputs run in
+# parallel.
+go test -run=FuzzSeed
+
+# When fuzzing, T.Parallel should be safe to call, but it should have no effect.
+# We just check that it doesn't hang, which would be the most obvious
+# failure mode.
+# TODO(jayconrod): check for the string "after T.Parallel". It's not printed
+# by 'go test', so we can't distinguish that crasher from some other panic.
+! go test -run=FuzzMutate -fuzz=FuzzMutate
+exists testdata/fuzz/FuzzMutate
+
+# Testdata should now contain a corpus entry which will fail FuzzMutate.
+# Run the test without fuzzing, setting -parallel to different values to make
+# sure it fails, and doesn't hang.
+! go test -run=FuzzMutate -parallel=1
+! go test -run=FuzzMutate -parallel=2
+! go test -run=FuzzMutate -parallel=4
+
+-- go.mod --
+module fuzz_parallel
+
+go 1.17
+-- fuzz_parallel_test.go --
+package fuzz_parallel
+
+import (
+       "sort"
+       "sync"
+       "testing"
+)
+
+func FuzzSeed(f *testing.F) {
+       for _, v := range [][]byte{{'a'}, {'b'}, {'c'}} {
+               f.Add(v)
+       }
+
+       var mu sync.Mutex
+       var before, after []byte
+       f.Cleanup(func() {
+               sort.Slice(after, func(i, j int) bool { return after[i] < after[j] })
+               got := string(before) + string(after)
+               want := "abcabc"
+               if got != want {
+                       f.Fatalf("got %q; want %q", got, want)
+               }
+       })
+
+       f.Fuzz(func(t *testing.T, b []byte) {
+               before = append(before, b...)
+               t.Parallel()
+               mu.Lock()
+               after = append(after, b...)
+               mu.Unlock()
+       })
+}
+
+func FuzzMutate(f *testing.F) {
+       f.Fuzz(func(t *testing.T, _ []byte) {
+               t.Parallel()
+               t.Error("after T.Parallel")
+       })
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_profile_flags.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_profile_flags.txt
new file mode 100644 (file)
index 0000000..5434c72
--- /dev/null
@@ -0,0 +1,38 @@
+[!fuzz] skip
+
+! go test -fuzz=FuzzTrivial -coverprofile=prof
+! stdout .
+stderr '^cannot use -coverprofile flag with -fuzz flag$'
+
+! go test -fuzz=FuzzTrivial -blockprofile=prof
+! stdout .
+stderr '^cannot use -blockprofile flag with -fuzz flag$'
+
+! go test -fuzz=FuzzTrivial -cpuprofile=prof
+! stdout .
+stderr '^cannot use -cpuprofile flag with -fuzz flag$'
+
+! go test -fuzz=FuzzTrivial -memprofile=prof
+! stdout .
+stderr '^cannot use -memprofile flag with -fuzz flag$'
+
+! go test -fuzz=FuzzTrivial -mutexprofile=prof
+! stdout .
+stderr '^cannot use -mutexprofile flag with -fuzz flag$'
+
+! go test -fuzz=FuzzTrivial -trace=prof
+! stdout .
+stderr '^cannot use -trace flag with -fuzz flag$'
+
+-- go.mod --
+module example
+
+go 1.18
+-- fuzz_test.go --
+package example
+
+import "testing"
+
+func FuzzTrivial(f *testing.F) {
+       f.Fuzz(func(t *testing.T, _ []byte) {})
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_return.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_return.txt
new file mode 100644 (file)
index 0000000..c0540ef
--- /dev/null
@@ -0,0 +1,21 @@
+[short] skip
+
+# Disable vet, as its "tests" analyzer would report the same problem statically.
+
+! go test -vet=off .
+stdout '^panic: testing: fuzz target must not return a value \[recovered, repanicked\]$'
+
+-- go.mod --
+module test
+go 1.18
+-- x_test.go --
+package test
+
+import "testing"
+
+func Fuzz_returnErr(f *testing.F) {
+       f.Add("hello, validation!")
+       f.Fuzz(func(t *testing.T, in string) string {
+               return in
+       })
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_run.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_run.txt
new file mode 100644 (file)
index 0000000..99a4413
--- /dev/null
@@ -0,0 +1,143 @@
+[!fuzz] skip
+[short] skip
+env GOCACHE=$WORK/cache
+
+# Tests which verify the behavior and command line output when
+# running a fuzz target as a unit test.
+
+# Tests without -run.
+
+! go test
+stdout FAIL
+stdout 'error here'
+
+! go test -v
+stdout FAIL
+stdout 'error here'
+stdout '=== RUN   FuzzFoo/thisfails'
+stdout '--- FAIL: FuzzFoo/thisfails'
+stdout '=== RUN   FuzzFoo/thispasses'
+stdout '--- PASS: FuzzFoo/thispasses'
+
+# Tests where -run matches all seed corpora.
+
+! go test -run FuzzFoo/this
+stdout FAIL
+stdout 'error here'
+! stdout 'no tests to run'
+
+! go test -run /this
+stdout FAIL
+stdout 'error here'
+! stdout 'no tests to run'
+
+! go test -v -run FuzzFoo/this
+stdout FAIL
+stdout 'error here'
+stdout '=== RUN   FuzzFoo/thisfails'
+stdout '--- FAIL: FuzzFoo/thisfails'
+stdout '=== RUN   FuzzFoo/thispasses'
+stdout '--- PASS: FuzzFoo/thispasses'
+! stdout 'no tests to run'
+
+! go test -v -run /this
+stdout FAIL
+stdout 'error here'
+stdout '=== RUN   FuzzFoo/thisfails'
+stdout '--- FAIL: FuzzFoo/thisfails'
+stdout '=== RUN   FuzzFoo/thispasses'
+stdout '--- PASS: FuzzFoo/thispasses'
+! stdout 'no tests to run'
+
+# Tests where -run only matches one seed corpus which passes.
+
+go test -run FuzzFoo/thispasses
+stdout ok
+! stdout 'no tests to run'
+
+go test -run /thispasses
+stdout ok
+! stdout 'no tests to run'
+
+# Same tests in verbose mode
+go test -v -run FuzzFoo/thispasses
+stdout '=== RUN   FuzzFoo/thispasses'
+stdout '--- PASS: FuzzFoo/thispasses'
+! stdout '=== RUN   FuzzFoo/thisfails'
+! stdout 'no tests to run'
+
+go test -v -run /thispasses
+stdout '=== RUN   FuzzFoo/thispasses'
+stdout '--- PASS: FuzzFoo/thispasses'
+! stdout '=== RUN   FuzzFoo/thisfails'
+! stdout 'no tests to run'
+
+# Tests where -run only matches one seed corpus which fails.
+
+! go test -run FuzzFoo/thisfails
+stdout FAIL
+stdout 'error here'
+! stdout 'no tests to run'
+
+! go test -run /thisfails
+stdout FAIL
+stdout 'error here'
+! stdout 'no tests to run'
+
+! go test -v -run FuzzFoo/thisfails
+stdout 'error here'
+stdout '=== RUN   FuzzFoo/thisfails'
+stdout '--- FAIL: FuzzFoo/thisfails'
+! stdout '=== RUN   FuzzFoo/thispasses'
+! stdout 'no tests to run'
+
+! go test -v -run /thisfails
+stdout 'error here'
+stdout '=== RUN   FuzzFoo/thisfails'
+stdout '--- FAIL: FuzzFoo/thisfails'
+! stdout '=== RUN   FuzzFoo/thispasses'
+! stdout 'no tests to run'
+
+# Tests where -run doesn't match any seed corpora.
+
+go test -run FuzzFoo/nomatch
+stdout ok
+
+go test -run /nomatch
+stdout ok
+
+go test -v -run FuzzFoo/nomatch
+stdout '=== RUN   FuzzFoo'
+stdout '--- PASS: FuzzFoo'
+stdout ok
+! stdout 'no tests to run'
+
+go test -v -run /nomatch
+stdout '=== RUN   FuzzFoo'
+stdout '--- PASS: FuzzFoo'
+stdout ok
+! stdout 'no tests to run'
+
+-- go.mod --
+module example.com/x
+
+go 1.16
+-- x_test.go --
+package x
+
+import "testing"
+
+func FuzzFoo(f *testing.F) {
+    f.Add("this is fine")
+    f.Fuzz(func(t *testing.T, s string) {
+        if s == "fails" {
+            t.Error("error here")
+        }
+    })
+}
+-- testdata/fuzz/FuzzFoo/thisfails --
+go test fuzz v1
+string("fails")
+-- testdata/fuzz/FuzzFoo/thispasses --
+go test fuzz v1
+string("passes")
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_seed_corpus.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_seed_corpus.txt
new file mode 100644 (file)
index 0000000..8a7a24a
--- /dev/null
@@ -0,0 +1,203 @@
+[!fuzz-instrumented] skip
+[short] skip
+env GOCACHE=$WORK/cache
+
+# Test that fuzzing a target with a failure in f.Add prints the crash
+# and doesn't write anything to testdata/fuzz
+! go test -fuzz=^FuzzWithAdd$ -run=^FuzzWithAdd$ -fuzztime=1x
+! stdout ^ok
+! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithAdd[/\\]'
+stdout FAIL
+
+# Test that fuzzing a target with a success in f.Add and a fuzztime of only
+# 1 does not produce a crash.
+go test -fuzz=FuzzWithGoodAdd -run=FuzzWithGoodAdd -fuzztime=1x
+stdout ok
+! stdout FAIL
+
+# Test that fuzzing a target with a failure in testdata/fuzz prints the crash
+# and doesn't write anything to testdata/fuzz
+! go test -fuzz=FuzzWithTestdata -run=FuzzWithTestdata -fuzztime=1x
+! stdout ^ok
+! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithTestdata[/\\]'
+stdout 'failure while testing seed corpus entry: FuzzWithTestdata/1'
+stdout FAIL
+
+# Test that fuzzing a target with no seed corpus or cache finds a crash, prints
+# it, and write it to testdata
+! go test -fuzz=FuzzWithNoCache -run=FuzzWithNoCache -fuzztime=1x
+! stdout ^ok
+stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithNoCache[/\\]'
+stdout FAIL
+
+# Write a crashing input to the cache
+mkdir $GOCACHE/fuzz/example.com/x/FuzzWithCache
+cp cache-file $GOCACHE/fuzz/example.com/x/FuzzWithCache/1
+
+# Test that fuzzing a target with a failure in the cache prints the crash
+# and writes this as a "new" crash to testdata/fuzz
+! go test -fuzz=FuzzWithCache -run=FuzzWithCache -fuzztime=1x
+! stdout ^ok
+stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithCache[/\\]'
+stdout FAIL
+
+# Write a crashing input to the cache
+mkdir $GOCACHE/fuzz/example.com/x/FuzzWithMinimizableCache
+cp cache-file-bytes $GOCACHE/fuzz/example.com/x/FuzzWithMinimizableCache/1
+
+# Test that fuzzing a target with a failure in the cache minimizes it and writes
+# the new crash to testdata/fuzz
+! go test -fuzz=FuzzWithMinimizableCache -run=FuzzWithMinimizableCache -fuzztime=10000x
+! stdout ^ok
+stdout 'gathering baseline coverage'
+stdout 'got the minimum size!'
+stdout 'contains a non-zero byte of length 10'
+stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithMinimizableCache[/\\]'
+stdout FAIL
+# Make sure this crash didn't come from fuzzing
+# (the log line that states fuzzing began shouldn't have printed)
+! stdout 'execs'
+
+# Clear the fuzz cache and make sure it's gone
+go clean -fuzzcache
+! exists $GOCACHE/fuzz
+
+# The tests below should operate the exact same as the previous tests. If -fuzz
+# is enabled, then whatever target is going to be fuzzed shouldn't be run by
+# anything other than the workers.
+
+# Test that fuzzing a target (with -run=None set) with a failure in f.Add prints
+# the crash and doesn't write anything to testdata/fuzz -fuzztime=1x
+! go test -fuzz=^FuzzWithAdd$ -run=None
+! stdout ^ok
+! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithAdd[/\\]'
+stdout FAIL
+
+# Test that fuzzing a target (with -run=None set) with a success in f.Add and a
+# fuzztime of only 1 does not produce a crash.
+go test -fuzz=FuzzWithGoodAdd -run=None -fuzztime=1x
+stdout ok
+! stdout FAIL
+
+# Test that fuzzing a target (with -run=None set) with a failure in
+# testdata/fuzz prints the crash and doesn't write anything to testdata/fuzz
+! go test -fuzz=FuzzWithTestdata -run=None -fuzztime=1x
+! stdout ^ok
+! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithTestdata[/\\]'
+stdout FAIL
+
+# Write a crashing input to the cache
+mkdir $GOCACHE/fuzz/example.com/x/FuzzRunNoneWithCache
+cp cache-file $GOCACHE/fuzz/example.com/x/FuzzRunNoneWithCache/1
+
+# Test that fuzzing a target (with -run=None set) with a failure in the cache
+# prints the crash and writes this as a "new" crash to testdata/fuzz
+! go test -fuzz=FuzzRunNoneWithCache -run=None -fuzztime=1x
+! stdout ^ok
+stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzRunNoneWithCache[/\\]'
+stdout FAIL
+
+# Clear the fuzz cache and make sure it's gone
+go clean -fuzzcache
+! exists $GOCACHE/fuzz
+
+# The tests below should operate the exact same way for the previous tests with
+# a seed corpus (namely, they should still fail). However, the binary is built
+# without instrumentation, so this should be a "testing only" run which executes
+# the seed corpus before attempting to fuzz.
+
+go test -c
+! exec ./x.test$GOEXE -test.fuzz=^FuzzWithAdd$ -test.run=^FuzzWithAdd$ -test.fuzztime=1x -test.fuzzcachedir=$WORK/cache
+! stdout ^ok
+! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithAdd[/\\]'
+stdout FAIL
+stderr warning
+
+go test -c
+! exec ./x.test$GOEXE -test.fuzz=FuzzWithTestdata -test.run=^FuzzWithTestdata$ -test.fuzztime=1x -test.fuzzcachedir=$WORK/cache
+! stdout ^ok
+! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithTestdata[/\\]'
+stdout FAIL
+stderr warning
+
+-- go.mod --
+module example.com/x
+
+go 1.16
+-- x_test.go --
+package x
+
+import "testing"
+
+func FuzzWithAdd(f *testing.F) {
+    f.Add(10)
+    f.Fuzz(func(t *testing.T, i int) {
+        if i == 10 {
+            t.Error("bad thing here")
+        }
+    })
+}
+
+func FuzzWithGoodAdd(f *testing.F) {
+    f.Add(10)
+    f.Fuzz(func(t *testing.T, i int) {
+        if i != 10 {
+            t.Error("bad thing here")
+        }
+    })
+}
+
+func FuzzWithTestdata(f *testing.F) {
+    f.Fuzz(func(t *testing.T, i int) {
+        if i == 10 {
+            t.Error("bad thing here")
+        }
+    })
+}
+
+func FuzzWithNoCache(f *testing.F) {
+    f.Fuzz(func(t *testing.T, i int) {
+        t.Error("bad thing here")
+    })
+}
+
+func FuzzWithCache(f *testing.F) {
+    f.Fuzz(func(t *testing.T, i int) {
+        if i == 10 {
+            t.Error("bad thing here")
+        }
+    })
+}
+
+func FuzzWithMinimizableCache(f *testing.F) {
+    f.Fuzz(func(t *testing.T, b []byte) {
+               if len(b) < 10 {
+                       return
+               }
+               for _, n := range b {
+                       if n != 0 {
+                               if len(b) == 10 {
+                                       t.Log("got the minimum size!")
+                               }
+                               t.Fatalf("contains a non-zero byte of length %d", len(b))
+                       }
+               }
+    })
+}
+
+func FuzzRunNoneWithCache(f *testing.F) {
+    f.Fuzz(func(t *testing.T, i int) {
+        if i == 10 {
+            t.Error("bad thing here")
+        }
+    })
+}
+-- testdata/fuzz/FuzzWithTestdata/1 --
+go test fuzz v1
+int(10)
+-- cache-file --
+go test fuzz v1
+int(10)
+-- cache-file-bytes --
+go test fuzz v1
+[]byte("11111111111111111111")
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_setenv.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_setenv.txt
new file mode 100644 (file)
index 0000000..1370cd8
--- /dev/null
@@ -0,0 +1,46 @@
+[!fuzz] skip
+[short] skip
+env GOCACHE=$WORK/cache
+
+go test -fuzz=FuzzA -fuzztime=100x fuzz_setenv_test.go
+
+-- fuzz_setenv_test.go --
+package fuzz
+
+import (
+  "flag"
+  "os"
+  "testing"
+)
+
+func FuzzA(f *testing.F) {
+  if s := os.Getenv("TEST_FUZZ_SETENV_A"); isWorker() && s == "" {
+    f.Fatal("environment variable not set")
+  } else if !isWorker() && s != "" {
+    f.Fatal("environment variable already set")
+  }
+  f.Setenv("TEST_FUZZ_SETENV_A", "A")
+  if os.Getenv("TEST_FUZZ_SETENV_A") == "" {
+    f.Fatal("Setenv did not set environment variable")
+  }
+  f.Fuzz(func(*testing.T, []byte) {})
+}
+
+func FuzzB(f *testing.F) {
+  if os.Getenv("TEST_FUZZ_SETENV_A") != "" {
+    f.Fatal("environment variable not cleared after FuzzA")
+  }
+  f.Skip()
+}
+
+func isWorker() bool {
+       f := flag.Lookup("test.fuzzworker")
+       if f == nil {
+               return false
+       }
+       get, ok := f.Value.(flag.Getter)
+       if !ok {
+               return false
+       }
+       return get.Get() == interface{}(true)
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_test_race.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_test_race.txt
new file mode 100644 (file)
index 0000000..1bed47d
--- /dev/null
@@ -0,0 +1,40 @@
+# Test that when both race detection and coverage instrumentation are enabled,
+# and seed values are being executed, the race detector isn't mistakenly
+# triggered.
+
+[short] skip
+[!fuzz] skip
+[!race] skip
+env GOCACHE=$WORK/cache
+
+# Test with coverage instrumentation enabled (-fuzz) and race instrumentation
+# but without actually fuzzing the target (by using a non-matching pattern)
+go test -fuzz=xxx -race -v
+! stderr 'race detected during execution of test'
+
+# Test with just race instrumentation enabled
+go test -race -v
+! stderr 'race detected during execution of test'
+
+# Test with coverage and race instrumentation enabled, and a matching fuzz
+# pattern
+go test -fuzz=FuzzRace -race -v -fuzztime=200x
+! stderr 'race detected during execution of test'
+
+-- go.mod --
+module test
+
+-- race_test.go --
+package race
+
+import "testing"
+
+func FuzzRace(f *testing.F) {
+       for i := 0; i < 100; i++ {
+               f.Add(i)
+       }
+
+       f.Fuzz(func(t *testing.T, i int) {
+               t.Parallel()
+       })
+}
diff --git a/src/cmd/internal/fuzztest/testdata/script/test_fuzz_unsupported.txt b/src/cmd/internal/fuzztest/testdata/script/test_fuzz_unsupported.txt
new file mode 100644 (file)
index 0000000..1ed0b8a
--- /dev/null
@@ -0,0 +1,18 @@
+[fuzz] skip
+
+! go test -fuzz=. -fuzztime=1x
+! stdout .
+stderr '^-fuzz flag is not supported on '$GOOS'/'$GOARCH'$'
+
+-- go.mod --
+module example
+
+go 1.18
+-- fuzz_test.go --
+package example
+
+import "testing"
+
+func FuzzTrivial(f *testing.F) {
+       f.Fuzz(func(t *testing.T, _ []byte) {})
+}
index 8dff13e22edfc47be061bd6f401ae63ea0132345..2a909f656d5e896bfba2fe73f0363a28bb0a366f 100644 (file)
@@ -137,6 +137,9 @@ func RunToolScriptTest(t *testing.T, repls []ToolReplacement, scriptsdir string,
        env := os.Environ()
        prependToPath(env, filepath.Join(tgr, "bin"))
        env = setenv(env, "GOROOT", tgr)
+       // GOOS and GOARCH are expected to be set by the toolchain script conditions.
+       env = setenv(env, "GOOS", runtime.GOOS)
+       env = setenv(env, "GOARCH", runtime.GOARCH)
        for _, repl := range repls {
                // consistency check
                chunks := strings.Split(repl.EnvVar, "=")