summaryrefslogtreecommitdiffstats
path: root/src/testing
diff options
context:
space:
mode:
Diffstat (limited to 'src/testing')
-rw-r--r--src/testing/allocs.go45
-rw-r--r--src/testing/allocs_test.go29
-rw-r--r--src/testing/benchmark.go840
-rw-r--r--src/testing/benchmark_test.go213
-rw-r--r--src/testing/cover.go124
-rw-r--r--src/testing/example.go97
-rw-r--r--src/testing/export_test.go7
-rw-r--r--src/testing/flag_test.go89
-rw-r--r--src/testing/fstest/mapfs.go244
-rw-r--r--src/testing/fstest/mapfs_test.go47
-rw-r--r--src/testing/fstest/testfs.go624
-rw-r--r--src/testing/fstest/testfs_test.go78
-rw-r--r--src/testing/fuzz.go731
-rw-r--r--src/testing/helper_test.go116
-rw-r--r--src/testing/helperfuncs_test.go124
-rw-r--r--src/testing/internal/testdeps/deps.go199
-rw-r--r--src/testing/iotest/example_test.go22
-rw-r--r--src/testing/iotest/logger.go54
-rw-r--r--src/testing/iotest/logger_test.go153
-rw-r--r--src/testing/iotest/reader.go268
-rw-r--r--src/testing/iotest/reader_test.go261
-rw-r--r--src/testing/iotest/writer.go35
-rw-r--r--src/testing/iotest/writer_test.go39
-rw-r--r--src/testing/match.go317
-rw-r--r--src/testing/match_test.go263
-rw-r--r--src/testing/newcover.go59
-rw-r--r--src/testing/panic_test.go267
-rw-r--r--src/testing/quick/quick.go385
-rw-r--r--src/testing/quick/quick_test.go327
-rw-r--r--src/testing/run_example.go66
-rw-r--r--src/testing/run_example_wasm.go76
-rw-r--r--src/testing/slogtest/example_test.go44
-rw-r--r--src/testing/slogtest/run_test.go31
-rw-r--r--src/testing/slogtest/slogtest.go375
-rw-r--r--src/testing/sub_test.go992
-rw-r--r--src/testing/testing.go2409
-rw-r--r--src/testing/testing_other.go13
-rw-r--r--src/testing/testing_test.go814
-rw-r--r--src/testing/testing_windows.go32
39 files changed, 10909 insertions, 0 deletions
diff --git a/src/testing/allocs.go b/src/testing/allocs.go
new file mode 100644
index 0000000..1eeb2d4
--- /dev/null
+++ b/src/testing/allocs.go
@@ -0,0 +1,45 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testing
+
+import (
+ "runtime"
+)
+
+// AllocsPerRun returns the average number of allocations during calls to f.
+// Although the return value has type float64, it will always be an integral value.
+//
+// To compute the number of allocations, the function will first be run once as
+// a warm-up. The average number of allocations over the specified number of
+// runs will then be measured and returned.
+//
+// AllocsPerRun sets GOMAXPROCS to 1 during its measurement and will restore
+// it before returning.
+func AllocsPerRun(runs int, f func()) (avg float64) {
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
+
+ // Warm up the function
+ f()
+
+ // Measure the starting statistics
+ var memstats runtime.MemStats
+ runtime.ReadMemStats(&memstats)
+ mallocs := 0 - memstats.Mallocs
+
+ // Run the function the specified number of times
+ for i := 0; i < runs; i++ {
+ f()
+ }
+
+ // Read the final statistics
+ runtime.ReadMemStats(&memstats)
+ mallocs += memstats.Mallocs
+
+ // Average the mallocs over the runs (not counting the warm-up).
+ // We are forced to return a float64 because the API is silly, but do
+ // the division as integers so we can ask if AllocsPerRun()==1
+ // instead of AllocsPerRun()<2.
+ return float64(mallocs / uint64(runs))
+}
diff --git a/src/testing/allocs_test.go b/src/testing/allocs_test.go
new file mode 100644
index 0000000..bbd3ae7
--- /dev/null
+++ b/src/testing/allocs_test.go
@@ -0,0 +1,29 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testing_test
+
+import "testing"
+
+var global any
+
+var allocsPerRunTests = []struct {
+ name string
+ fn func()
+ allocs float64
+}{
+ {"alloc *byte", func() { global = new(*byte) }, 1},
+ {"alloc complex128", func() { global = new(complex128) }, 1},
+ {"alloc float64", func() { global = new(float64) }, 1},
+ {"alloc int32", func() { global = new(int32) }, 1},
+ {"alloc byte", func() { global = new(byte) }, 1},
+}
+
+func TestAllocsPerRun(t *testing.T) {
+ for _, tt := range allocsPerRunTests {
+ if allocs := testing.AllocsPerRun(100, tt.fn); allocs != tt.allocs {
+ t.Errorf("AllocsPerRun(100, %s) = %v, want %v", tt.name, allocs, tt.allocs)
+ }
+ }
+}
diff --git a/src/testing/benchmark.go b/src/testing/benchmark.go
new file mode 100644
index 0000000..9491213
--- /dev/null
+++ b/src/testing/benchmark.go
@@ -0,0 +1,840 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testing
+
+import (
+ "flag"
+ "fmt"
+ "internal/sysinfo"
+ "io"
+ "math"
+ "os"
+ "runtime"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "time"
+ "unicode"
+)
+
+func initBenchmarkFlags() {
+ matchBenchmarks = flag.String("test.bench", "", "run only benchmarks matching `regexp`")
+ benchmarkMemory = flag.Bool("test.benchmem", false, "print memory allocations for benchmarks")
+ flag.Var(&benchTime, "test.benchtime", "run each benchmark for duration `d` or N times if `d` is of the form Nx")
+}
+
+var (
+ matchBenchmarks *string
+ benchmarkMemory *bool
+
+ benchTime = durationOrCountFlag{d: 1 * time.Second} // changed during test of testing package
+)
+
+type durationOrCountFlag struct {
+ d time.Duration
+ n int
+ allowZero bool
+}
+
+func (f *durationOrCountFlag) String() string {
+ if f.n > 0 {
+ return fmt.Sprintf("%dx", f.n)
+ }
+ return f.d.String()
+}
+
+func (f *durationOrCountFlag) Set(s string) error {
+ if strings.HasSuffix(s, "x") {
+ n, err := strconv.ParseInt(s[:len(s)-1], 10, 0)
+ if err != nil || n < 0 || (!f.allowZero && n == 0) {
+ return fmt.Errorf("invalid count")
+ }
+ *f = durationOrCountFlag{n: int(n)}
+ return nil
+ }
+ d, err := time.ParseDuration(s)
+ if err != nil || d < 0 || (!f.allowZero && d == 0) {
+ return fmt.Errorf("invalid duration")
+ }
+ *f = durationOrCountFlag{d: d}
+ return nil
+}
+
+// Global lock to ensure only one benchmark runs at a time.
+var benchmarkLock sync.Mutex
+
+// Used for every benchmark for measuring memory.
+var memStats runtime.MemStats
+
+// InternalBenchmark is an internal type but exported because it is cross-package;
+// it is part of the implementation of the "go test" command.
+type InternalBenchmark struct {
+ Name string
+ F func(b *B)
+}
+
+// B is a type passed to [Benchmark] functions to manage benchmark
+// timing and to specify the number of iterations to run.
+//
+// A benchmark ends when its Benchmark function returns or calls any of the methods
+// FailNow, Fatal, Fatalf, SkipNow, Skip, or Skipf. Those methods must be called
+// only from the goroutine running the Benchmark function.
+// The other reporting methods, such as the variations of Log and Error,
+// may be called simultaneously from multiple goroutines.
+//
+// Like in tests, benchmark logs are accumulated during execution
+// and dumped to standard output when done. Unlike in tests, benchmark logs
+// are always printed, so as not to hide output whose existence may be
+// affecting benchmark results.
+type B struct {
+ common
+ importPath string // import path of the package containing the benchmark
+ context *benchContext
+ N int
+ previousN int // number of iterations in the previous run
+ previousDuration time.Duration // total duration of the previous run
+ benchFunc func(b *B)
+ benchTime durationOrCountFlag
+ bytes int64
+ missingBytes bool // one of the subbenchmarks does not have bytes set.
+ timerOn bool
+ showAllocResult bool
+ result BenchmarkResult
+ parallelism int // RunParallel creates parallelism*GOMAXPROCS goroutines
+ // The initial states of memStats.Mallocs and memStats.TotalAlloc.
+ startAllocs uint64
+ startBytes uint64
+ // The net total of this test after being run.
+ netAllocs uint64
+ netBytes uint64
+ // Extra metrics collected by ReportMetric.
+ extra map[string]float64
+}
+
+// StartTimer starts timing a test. This function is called automatically
+// before a benchmark starts, but it can also be used to resume timing after
+// a call to [B.StopTimer].
+func (b *B) StartTimer() {
+ if !b.timerOn {
+ runtime.ReadMemStats(&memStats)
+ b.startAllocs = memStats.Mallocs
+ b.startBytes = memStats.TotalAlloc
+ b.start = time.Now()
+ b.timerOn = true
+ }
+}
+
+// StopTimer stops timing a test. This can be used to pause the timer
+// while performing complex initialization that you don't
+// want to measure.
+func (b *B) StopTimer() {
+ if b.timerOn {
+ b.duration += time.Since(b.start)
+ runtime.ReadMemStats(&memStats)
+ b.netAllocs += memStats.Mallocs - b.startAllocs
+ b.netBytes += memStats.TotalAlloc - b.startBytes
+ b.timerOn = false
+ }
+}
+
+// ResetTimer zeroes the elapsed benchmark time and memory allocation counters
+// and deletes user-reported metrics.
+// It does not affect whether the timer is running.
+func (b *B) ResetTimer() {
+ if b.extra == nil {
+ // Allocate the extra map before reading memory stats.
+ // Pre-size it to make more allocation unlikely.
+ b.extra = make(map[string]float64, 16)
+ } else {
+ clear(b.extra)
+ }
+ if b.timerOn {
+ runtime.ReadMemStats(&memStats)
+ b.startAllocs = memStats.Mallocs
+ b.startBytes = memStats.TotalAlloc
+ b.start = time.Now()
+ }
+ b.duration = 0
+ b.netAllocs = 0
+ b.netBytes = 0
+}
+
+// SetBytes records the number of bytes processed in a single operation.
+// If this is called, the benchmark will report ns/op and MB/s.
+func (b *B) SetBytes(n int64) { b.bytes = n }
+
+// ReportAllocs enables malloc statistics for this benchmark.
+// It is equivalent to setting -test.benchmem, but it only affects the
+// benchmark function that calls ReportAllocs.
+func (b *B) ReportAllocs() {
+ b.showAllocResult = true
+}
+
+// runN runs a single benchmark for the specified number of iterations.
+func (b *B) runN(n int) {
+ benchmarkLock.Lock()
+ defer benchmarkLock.Unlock()
+ defer func() {
+ b.runCleanup(normalPanic)
+ b.checkRaces()
+ }()
+ // Try to get a comparable environment for each run
+ // by clearing garbage from previous runs.
+ runtime.GC()
+ b.resetRaces()
+ b.N = n
+ b.parallelism = 1
+ b.ResetTimer()
+ b.StartTimer()
+ b.benchFunc(b)
+ b.StopTimer()
+ b.previousN = n
+ b.previousDuration = b.duration
+}
+
+// run1 runs the first iteration of benchFunc. It reports whether more
+// iterations of this benchmarks should be run.
+func (b *B) run1() bool {
+ if ctx := b.context; ctx != nil {
+ // Extend maxLen, if needed.
+ if n := len(b.name) + ctx.extLen + 1; n > ctx.maxLen {
+ ctx.maxLen = n + 8 // Add additional slack to avoid too many jumps in size.
+ }
+ }
+ go func() {
+ // Signal that we're done whether we return normally
+ // or by FailNow's runtime.Goexit.
+ defer func() {
+ b.signal <- true
+ }()
+
+ b.runN(1)
+ }()
+ <-b.signal
+ if b.failed {
+ fmt.Fprintf(b.w, "%s--- FAIL: %s\n%s", b.chatty.prefix(), b.name, b.output)
+ return false
+ }
+ // Only print the output if we know we are not going to proceed.
+ // Otherwise it is printed in processBench.
+ b.mu.RLock()
+ finished := b.finished
+ b.mu.RUnlock()
+ if b.hasSub.Load() || finished {
+ tag := "BENCH"
+ if b.skipped {
+ tag = "SKIP"
+ }
+ if b.chatty != nil && (len(b.output) > 0 || finished) {
+ b.trimOutput()
+ fmt.Fprintf(b.w, "%s--- %s: %s\n%s", b.chatty.prefix(), tag, b.name, b.output)
+ }
+ return false
+ }
+ return true
+}
+
+var labelsOnce sync.Once
+
+// run executes the benchmark in a separate goroutine, including all of its
+// subbenchmarks. b must not have subbenchmarks.
+func (b *B) run() {
+ labelsOnce.Do(func() {
+ fmt.Fprintf(b.w, "goos: %s\n", runtime.GOOS)
+ fmt.Fprintf(b.w, "goarch: %s\n", runtime.GOARCH)
+ if b.importPath != "" {
+ fmt.Fprintf(b.w, "pkg: %s\n", b.importPath)
+ }
+ if cpu := sysinfo.CPUName(); cpu != "" {
+ fmt.Fprintf(b.w, "cpu: %s\n", cpu)
+ }
+ })
+ if b.context != nil {
+ // Running go test --test.bench
+ b.context.processBench(b) // Must call doBench.
+ } else {
+ // Running func Benchmark.
+ b.doBench()
+ }
+}
+
+func (b *B) doBench() BenchmarkResult {
+ go b.launch()
+ <-b.signal
+ return b.result
+}
+
+// launch launches the benchmark function. It gradually increases the number
+// of benchmark iterations until the benchmark runs for the requested benchtime.
+// launch is run by the doBench function as a separate goroutine.
+// run1 must have been called on b.
+func (b *B) launch() {
+ // Signal that we're done whether we return normally
+ // or by FailNow's runtime.Goexit.
+ defer func() {
+ b.signal <- true
+ }()
+
+ // Run the benchmark for at least the specified amount of time.
+ if b.benchTime.n > 0 {
+ // We already ran a single iteration in run1.
+ // If -benchtime=1x was requested, use that result.
+ // See https://golang.org/issue/32051.
+ if b.benchTime.n > 1 {
+ b.runN(b.benchTime.n)
+ }
+ } else {
+ d := b.benchTime.d
+ for n := int64(1); !b.failed && b.duration < d && n < 1e9; {
+ last := n
+ // Predict required iterations.
+ goalns := d.Nanoseconds()
+ prevIters := int64(b.N)
+ prevns := b.duration.Nanoseconds()
+ if prevns <= 0 {
+ // Round up, to avoid div by zero.
+ prevns = 1
+ }
+ // Order of operations matters.
+ // For very fast benchmarks, prevIters ~= prevns.
+ // If you divide first, you get 0 or 1,
+ // which can hide an order of magnitude in execution time.
+ // So multiply first, then divide.
+ n = goalns * prevIters / prevns
+ // Run more iterations than we think we'll need (1.2x).
+ n += n / 5
+ // Don't grow too fast in case we had timing errors previously.
+ n = min(n, 100*last)
+ // Be sure to run at least one more than last time.
+ n = max(n, last+1)
+ // Don't run more than 1e9 times. (This also keeps n in int range on 32 bit platforms.)
+ n = min(n, 1e9)
+ b.runN(int(n))
+ }
+ }
+ b.result = BenchmarkResult{b.N, b.duration, b.bytes, b.netAllocs, b.netBytes, b.extra}
+}
+
+// Elapsed returns the measured elapsed time of the benchmark.
+// The duration reported by Elapsed matches the one measured by
+// [B.StartTimer], [B.StopTimer], and [B.ResetTimer].
+func (b *B) Elapsed() time.Duration {
+ d := b.duration
+ if b.timerOn {
+ d += time.Since(b.start)
+ }
+ return d
+}
+
+// ReportMetric adds "n unit" to the reported benchmark results.
+// If the metric is per-iteration, the caller should divide by b.N,
+// and by convention units should end in "/op".
+// ReportMetric overrides any previously reported value for the same unit.
+// ReportMetric panics if unit is the empty string or if unit contains
+// any whitespace.
+// If unit is a unit normally reported by the benchmark framework itself
+// (such as "allocs/op"), ReportMetric will override that metric.
+// Setting "ns/op" to 0 will suppress that built-in metric.
+func (b *B) ReportMetric(n float64, unit string) {
+ if unit == "" {
+ panic("metric unit must not be empty")
+ }
+ if strings.IndexFunc(unit, unicode.IsSpace) >= 0 {
+ panic("metric unit must not contain whitespace")
+ }
+ b.extra[unit] = n
+}
+
+// BenchmarkResult contains the results of a benchmark run.
+type BenchmarkResult struct {
+ N int // The number of iterations.
+ T time.Duration // The total time taken.
+ Bytes int64 // Bytes processed in one iteration.
+ MemAllocs uint64 // The total number of memory allocations.
+ MemBytes uint64 // The total number of bytes allocated.
+
+ // Extra records additional metrics reported by ReportMetric.
+ Extra map[string]float64
+}
+
+// NsPerOp returns the "ns/op" metric.
+func (r BenchmarkResult) NsPerOp() int64 {
+ if v, ok := r.Extra["ns/op"]; ok {
+ return int64(v)
+ }
+ if r.N <= 0 {
+ return 0
+ }
+ return r.T.Nanoseconds() / int64(r.N)
+}
+
+// mbPerSec returns the "MB/s" metric.
+func (r BenchmarkResult) mbPerSec() float64 {
+ if v, ok := r.Extra["MB/s"]; ok {
+ return v
+ }
+ if r.Bytes <= 0 || r.T <= 0 || r.N <= 0 {
+ return 0
+ }
+ return (float64(r.Bytes) * float64(r.N) / 1e6) / r.T.Seconds()
+}
+
+// AllocsPerOp returns the "allocs/op" metric,
+// which is calculated as r.MemAllocs / r.N.
+func (r BenchmarkResult) AllocsPerOp() int64 {
+ if v, ok := r.Extra["allocs/op"]; ok {
+ return int64(v)
+ }
+ if r.N <= 0 {
+ return 0
+ }
+ return int64(r.MemAllocs) / int64(r.N)
+}
+
+// AllocedBytesPerOp returns the "B/op" metric,
+// which is calculated as r.MemBytes / r.N.
+func (r BenchmarkResult) AllocedBytesPerOp() int64 {
+ if v, ok := r.Extra["B/op"]; ok {
+ return int64(v)
+ }
+ if r.N <= 0 {
+ return 0
+ }
+ return int64(r.MemBytes) / int64(r.N)
+}
+
+// String returns a summary of the benchmark results.
+// It follows the benchmark result line format from
+// https://golang.org/design/14313-benchmark-format, not including the
+// benchmark name.
+// Extra metrics override built-in metrics of the same name.
+// String does not include allocs/op or B/op, since those are reported
+// by [BenchmarkResult.MemString].
+func (r BenchmarkResult) String() string {
+ buf := new(strings.Builder)
+ fmt.Fprintf(buf, "%8d", r.N)
+
+ // Get ns/op as a float.
+ ns, ok := r.Extra["ns/op"]
+ if !ok {
+ ns = float64(r.T.Nanoseconds()) / float64(r.N)
+ }
+ if ns != 0 {
+ buf.WriteByte('\t')
+ prettyPrint(buf, ns, "ns/op")
+ }
+
+ if mbs := r.mbPerSec(); mbs != 0 {
+ fmt.Fprintf(buf, "\t%7.2f MB/s", mbs)
+ }
+
+ // Print extra metrics that aren't represented in the standard
+ // metrics.
+ var extraKeys []string
+ for k := range r.Extra {
+ switch k {
+ case "ns/op", "MB/s", "B/op", "allocs/op":
+ // Built-in metrics reported elsewhere.
+ continue
+ }
+ extraKeys = append(extraKeys, k)
+ }
+ sort.Strings(extraKeys)
+ for _, k := range extraKeys {
+ buf.WriteByte('\t')
+ prettyPrint(buf, r.Extra[k], k)
+ }
+ return buf.String()
+}
+
+func prettyPrint(w io.Writer, x float64, unit string) {
+ // Print all numbers with 10 places before the decimal point
+ // and small numbers with four sig figs. Field widths are
+ // chosen to fit the whole part in 10 places while aligning
+ // the decimal point of all fractional formats.
+ var format string
+ switch y := math.Abs(x); {
+ case y == 0 || y >= 999.95:
+ format = "%10.0f %s"
+ case y >= 99.995:
+ format = "%12.1f %s"
+ case y >= 9.9995:
+ format = "%13.2f %s"
+ case y >= 0.99995:
+ format = "%14.3f %s"
+ case y >= 0.099995:
+ format = "%15.4f %s"
+ case y >= 0.0099995:
+ format = "%16.5f %s"
+ case y >= 0.00099995:
+ format = "%17.6f %s"
+ default:
+ format = "%18.7f %s"
+ }
+ fmt.Fprintf(w, format, x, unit)
+}
+
+// MemString returns r.AllocedBytesPerOp and r.AllocsPerOp in the same format as 'go test'.
+func (r BenchmarkResult) MemString() string {
+ return fmt.Sprintf("%8d B/op\t%8d allocs/op",
+ r.AllocedBytesPerOp(), r.AllocsPerOp())
+}
+
+// benchmarkName returns full name of benchmark including procs suffix.
+func benchmarkName(name string, n int) string {
+ if n != 1 {
+ return fmt.Sprintf("%s-%d", name, n)
+ }
+ return name
+}
+
+type benchContext struct {
+ match *matcher
+
+ maxLen int // The largest recorded benchmark name.
+ extLen int // Maximum extension length.
+}
+
+// RunBenchmarks is an internal function but exported because it is cross-package;
+// it is part of the implementation of the "go test" command.
+func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark) {
+ runBenchmarks("", matchString, benchmarks)
+}
+
+func runBenchmarks(importPath string, matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark) bool {
+ // If no flag was specified, don't run benchmarks.
+ if len(*matchBenchmarks) == 0 {
+ return true
+ }
+ // Collect matching benchmarks and determine longest name.
+ maxprocs := 1
+ for _, procs := range cpuList {
+ if procs > maxprocs {
+ maxprocs = procs
+ }
+ }
+ ctx := &benchContext{
+ match: newMatcher(matchString, *matchBenchmarks, "-test.bench", *skip),
+ extLen: len(benchmarkName("", maxprocs)),
+ }
+ var bs []InternalBenchmark
+ for _, Benchmark := range benchmarks {
+ if _, matched, _ := ctx.match.fullName(nil, Benchmark.Name); matched {
+ bs = append(bs, Benchmark)
+ benchName := benchmarkName(Benchmark.Name, maxprocs)
+ if l := len(benchName) + ctx.extLen + 1; l > ctx.maxLen {
+ ctx.maxLen = l
+ }
+ }
+ }
+ main := &B{
+ common: common{
+ name: "Main",
+ w: os.Stdout,
+ bench: true,
+ },
+ importPath: importPath,
+ benchFunc: func(b *B) {
+ for _, Benchmark := range bs {
+ b.Run(Benchmark.Name, Benchmark.F)
+ }
+ },
+ benchTime: benchTime,
+ context: ctx,
+ }
+ if Verbose() {
+ main.chatty = newChattyPrinter(main.w)
+ }
+ main.runN(1)
+ return !main.failed
+}
+
+// processBench runs bench b for the configured CPU counts and prints the results.
+func (ctx *benchContext) processBench(b *B) {
+ for i, procs := range cpuList {
+ for j := uint(0); j < *count; j++ {
+ runtime.GOMAXPROCS(procs)
+ benchName := benchmarkName(b.name, procs)
+
+ // If it's chatty, we've already printed this information.
+ if b.chatty == nil {
+ fmt.Fprintf(b.w, "%-*s\t", ctx.maxLen, benchName)
+ }
+ // Recompute the running time for all but the first iteration.
+ if i > 0 || j > 0 {
+ b = &B{
+ common: common{
+ signal: make(chan bool),
+ name: b.name,
+ w: b.w,
+ chatty: b.chatty,
+ bench: true,
+ },
+ benchFunc: b.benchFunc,
+ benchTime: b.benchTime,
+ }
+ b.run1()
+ }
+ r := b.doBench()
+ if b.failed {
+ // The output could be very long here, but probably isn't.
+ // We print it all, regardless, because we don't want to trim the reason
+ // the benchmark failed.
+ fmt.Fprintf(b.w, "%s--- FAIL: %s\n%s", b.chatty.prefix(), benchName, b.output)
+ continue
+ }
+ results := r.String()
+ if b.chatty != nil {
+ fmt.Fprintf(b.w, "%-*s\t", ctx.maxLen, benchName)
+ }
+ if *benchmarkMemory || b.showAllocResult {
+ results += "\t" + r.MemString()
+ }
+ fmt.Fprintln(b.w, results)
+ // Unlike with tests, we ignore the -chatty flag and always print output for
+ // benchmarks since the output generation time will skew the results.
+ if len(b.output) > 0 {
+ b.trimOutput()
+ fmt.Fprintf(b.w, "%s--- BENCH: %s\n%s", b.chatty.prefix(), benchName, b.output)
+ }
+ if p := runtime.GOMAXPROCS(-1); p != procs {
+ fmt.Fprintf(os.Stderr, "testing: %s left GOMAXPROCS set to %d\n", benchName, p)
+ }
+ if b.chatty != nil && b.chatty.json {
+ b.chatty.Updatef("", "=== NAME %s\n", "")
+ }
+ }
+ }
+}
+
+// If hideStdoutForTesting is true, Run does not print the benchName.
+// This avoids a spurious print during 'go test' on package testing itself,
+// which invokes b.Run in its own tests (see sub_test.go).
+var hideStdoutForTesting = false
+
+// Run benchmarks f as a subbenchmark with the given name. It reports
+// whether there were any failures.
+//
+// A subbenchmark is like any other benchmark. A benchmark that calls Run at
+// least once will not be measured itself and will be called once with N=1.
+func (b *B) Run(name string, f func(b *B)) bool {
+ // Since b has subbenchmarks, we will no longer run it as a benchmark itself.
+ // Release the lock and acquire it on exit to ensure locks stay paired.
+ b.hasSub.Store(true)
+ benchmarkLock.Unlock()
+ defer benchmarkLock.Lock()
+
+ benchName, ok, partial := b.name, true, false
+ if b.context != nil {
+ benchName, ok, partial = b.context.match.fullName(&b.common, name)
+ }
+ if !ok {
+ return true
+ }
+ var pc [maxStackLen]uintptr
+ n := runtime.Callers(2, pc[:])
+ sub := &B{
+ common: common{
+ signal: make(chan bool),
+ name: benchName,
+ parent: &b.common,
+ level: b.level + 1,
+ creator: pc[:n],
+ w: b.w,
+ chatty: b.chatty,
+ bench: true,
+ },
+ importPath: b.importPath,
+ benchFunc: f,
+ benchTime: b.benchTime,
+ context: b.context,
+ }
+ if partial {
+ // Partial name match, like -bench=X/Y matching BenchmarkX.
+ // Only process sub-benchmarks, if any.
+ sub.hasSub.Store(true)
+ }
+
+ if b.chatty != nil {
+ labelsOnce.Do(func() {
+ fmt.Printf("goos: %s\n", runtime.GOOS)
+ fmt.Printf("goarch: %s\n", runtime.GOARCH)
+ if b.importPath != "" {
+ fmt.Printf("pkg: %s\n", b.importPath)
+ }
+ if cpu := sysinfo.CPUName(); cpu != "" {
+ fmt.Printf("cpu: %s\n", cpu)
+ }
+ })
+
+ if !hideStdoutForTesting {
+ if b.chatty.json {
+ b.chatty.Updatef(benchName, "=== RUN %s\n", benchName)
+ }
+ fmt.Println(benchName)
+ }
+ }
+
+ if sub.run1() {
+ sub.run()
+ }
+ b.add(sub.result)
+ return !sub.failed
+}
+
+// add simulates running benchmarks in sequence in a single iteration. It is
+// used to give some meaningful results in case func Benchmark is used in
+// combination with Run.
+func (b *B) add(other BenchmarkResult) {
+ r := &b.result
+ // The aggregated BenchmarkResults resemble running all subbenchmarks as
+ // in sequence in a single benchmark.
+ r.N = 1
+ r.T += time.Duration(other.NsPerOp())
+ if other.Bytes == 0 {
+ // Summing Bytes is meaningless in aggregate if not all subbenchmarks
+ // set it.
+ b.missingBytes = true
+ r.Bytes = 0
+ }
+ if !b.missingBytes {
+ r.Bytes += other.Bytes
+ }
+ r.MemAllocs += uint64(other.AllocsPerOp())
+ r.MemBytes += uint64(other.AllocedBytesPerOp())
+}
+
+// trimOutput shortens the output from a benchmark, which can be very long.
+func (b *B) trimOutput() {
+ // The output is likely to appear multiple times because the benchmark
+ // is run multiple times, but at least it will be seen. This is not a big deal
+ // because benchmarks rarely print, but just in case, we trim it if it's too long.
+ const maxNewlines = 10
+ for nlCount, j := 0, 0; j < len(b.output); j++ {
+ if b.output[j] == '\n' {
+ nlCount++
+ if nlCount >= maxNewlines {
+ b.output = append(b.output[:j], "\n\t... [output truncated]\n"...)
+ break
+ }
+ }
+ }
+}
+
+// A PB is used by RunParallel for running parallel benchmarks.
+type PB struct {
+ globalN *atomic.Uint64 // shared between all worker goroutines iteration counter
+ grain uint64 // acquire that many iterations from globalN at once
+ cache uint64 // local cache of acquired iterations
+ bN uint64 // total number of iterations to execute (b.N)
+}
+
+// Next reports whether there are more iterations to execute.
+func (pb *PB) Next() bool {
+ if pb.cache == 0 {
+ n := pb.globalN.Add(pb.grain)
+ if n <= pb.bN {
+ pb.cache = pb.grain
+ } else if n < pb.bN+pb.grain {
+ pb.cache = pb.bN + pb.grain - n
+ } else {
+ return false
+ }
+ }
+ pb.cache--
+ return true
+}
+
+// RunParallel runs a benchmark in parallel.
+// It creates multiple goroutines and distributes b.N iterations among them.
+// The number of goroutines defaults to GOMAXPROCS. To increase parallelism for
+// non-CPU-bound benchmarks, call [B.SetParallelism] before RunParallel.
+// RunParallel is usually used with the go test -cpu flag.
+//
+// The body function will be run in each goroutine. It should set up any
+// goroutine-local state and then iterate until pb.Next returns false.
+// It should not use the [B.StartTimer], [B.StopTimer], or [B.ResetTimer] functions,
+// because they have global effect. It should also not call [B.Run].
+//
+// RunParallel reports ns/op values as wall time for the benchmark as a whole,
+// not the sum of wall time or CPU time over each parallel goroutine.
+func (b *B) RunParallel(body func(*PB)) {
+ if b.N == 0 {
+ return // Nothing to do when probing.
+ }
+ // Calculate grain size as number of iterations that take ~100µs.
+ // 100µs is enough to amortize the overhead and provide sufficient
+ // dynamic load balancing.
+ grain := uint64(0)
+ if b.previousN > 0 && b.previousDuration > 0 {
+ grain = 1e5 * uint64(b.previousN) / uint64(b.previousDuration)
+ }
+ if grain < 1 {
+ grain = 1
+ }
+ // We expect the inner loop and function call to take at least 10ns,
+ // so do not do more than 100µs/10ns=1e4 iterations.
+ if grain > 1e4 {
+ grain = 1e4
+ }
+
+ var n atomic.Uint64
+ numProcs := b.parallelism * runtime.GOMAXPROCS(0)
+ var wg sync.WaitGroup
+ wg.Add(numProcs)
+ for p := 0; p < numProcs; p++ {
+ go func() {
+ defer wg.Done()
+ pb := &PB{
+ globalN: &n,
+ grain: grain,
+ bN: uint64(b.N),
+ }
+ body(pb)
+ }()
+ }
+ wg.Wait()
+ if n.Load() <= uint64(b.N) && !b.Failed() {
+ b.Fatal("RunParallel: body exited without pb.Next() == false")
+ }
+}
+
+// SetParallelism sets the number of goroutines used by [B.RunParallel] to p*GOMAXPROCS.
+// There is usually no need to call SetParallelism for CPU-bound benchmarks.
+// If p is less than 1, this call will have no effect.
+func (b *B) SetParallelism(p int) {
+ if p >= 1 {
+ b.parallelism = p
+ }
+}
+
+// Benchmark benchmarks a single function. It is useful for creating
+// custom benchmarks that do not use the "go test" command.
+//
+// If f depends on testing flags, then [Init] must be used to register
+// those flags before calling Benchmark and before calling [flag.Parse].
+//
+// If f calls Run, the result will be an estimate of running all its
+// subbenchmarks that don't call Run in sequence in a single benchmark.
+func Benchmark(f func(b *B)) BenchmarkResult {
+ b := &B{
+ common: common{
+ signal: make(chan bool),
+ w: discard{},
+ },
+ benchFunc: f,
+ benchTime: benchTime,
+ }
+ if b.run1() {
+ b.run()
+ }
+ return b.result
+}
+
+type discard struct{}
+
+func (discard) Write(b []byte) (n int, err error) { return len(b), nil }
diff --git a/src/testing/benchmark_test.go b/src/testing/benchmark_test.go
new file mode 100644
index 0000000..2987170
--- /dev/null
+++ b/src/testing/benchmark_test.go
@@ -0,0 +1,213 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testing_test
+
+import (
+ "bytes"
+ "runtime"
+ "sort"
+ "strings"
+ "sync/atomic"
+ "testing"
+ "text/template"
+ "time"
+)
+
+var prettyPrintTests = []struct {
+ v float64
+ expected string
+}{
+ {0, " 0 x"},
+ {1234.1, " 1234 x"},
+ {-1234.1, " -1234 x"},
+ {999.950001, " 1000 x"},
+ {999.949999, " 999.9 x"},
+ {99.9950001, " 100.0 x"},
+ {99.9949999, " 99.99 x"},
+ {-99.9949999, " -99.99 x"},
+ {0.000999950001, " 0.001000 x"},
+ {0.000999949999, " 0.0009999 x"}, // smallest case
+ {0.0000999949999, " 0.0001000 x"},
+}
+
+func TestPrettyPrint(t *testing.T) {
+ for _, tt := range prettyPrintTests {
+ buf := new(strings.Builder)
+ testing.PrettyPrint(buf, tt.v, "x")
+ if tt.expected != buf.String() {
+ t.Errorf("prettyPrint(%v): expected %q, actual %q", tt.v, tt.expected, buf.String())
+ }
+ }
+}
+
+func TestResultString(t *testing.T) {
+ // Test fractional ns/op handling
+ r := testing.BenchmarkResult{
+ N: 100,
+ T: 240 * time.Nanosecond,
+ }
+ if r.NsPerOp() != 2 {
+ t.Errorf("NsPerOp: expected 2, actual %v", r.NsPerOp())
+ }
+ if want, got := " 100\t 2.400 ns/op", r.String(); want != got {
+ t.Errorf("String: expected %q, actual %q", want, got)
+ }
+
+ // Test sub-1 ns/op (issue #31005)
+ r.T = 40 * time.Nanosecond
+ if want, got := " 100\t 0.4000 ns/op", r.String(); want != got {
+ t.Errorf("String: expected %q, actual %q", want, got)
+ }
+
+ // Test 0 ns/op
+ r.T = 0
+ if want, got := " 100", r.String(); want != got {
+ t.Errorf("String: expected %q, actual %q", want, got)
+ }
+}
+
+func TestRunParallel(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping in short mode")
+ }
+ testing.Benchmark(func(b *testing.B) {
+ procs := uint32(0)
+ iters := uint64(0)
+ b.SetParallelism(3)
+ b.RunParallel(func(pb *testing.PB) {
+ atomic.AddUint32(&procs, 1)
+ for pb.Next() {
+ atomic.AddUint64(&iters, 1)
+ }
+ })
+ if want := uint32(3 * runtime.GOMAXPROCS(0)); procs != want {
+ t.Errorf("got %v procs, want %v", procs, want)
+ }
+ if iters != uint64(b.N) {
+ t.Errorf("got %v iters, want %v", iters, b.N)
+ }
+ })
+}
+
+func TestRunParallelFail(t *testing.T) {
+ testing.Benchmark(func(b *testing.B) {
+ b.RunParallel(func(pb *testing.PB) {
+ // The function must be able to log/abort
+ // w/o crashing/deadlocking the whole benchmark.
+ b.Log("log")
+ b.Error("error")
+ })
+ })
+}
+
+func TestRunParallelFatal(t *testing.T) {
+ testing.Benchmark(func(b *testing.B) {
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ if b.N > 1 {
+ b.Fatal("error")
+ }
+ }
+ })
+ })
+}
+
+func TestRunParallelSkipNow(t *testing.T) {
+ testing.Benchmark(func(b *testing.B) {
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ if b.N > 1 {
+ b.SkipNow()
+ }
+ }
+ })
+ })
+}
+
+func ExampleB_RunParallel() {
+ // Parallel benchmark for text/template.Template.Execute on a single object.
+ testing.Benchmark(func(b *testing.B) {
+ templ := template.Must(template.New("test").Parse("Hello, {{.}}!"))
+ // RunParallel will create GOMAXPROCS goroutines
+ // and distribute work among them.
+ b.RunParallel(func(pb *testing.PB) {
+ // Each goroutine has its own bytes.Buffer.
+ var buf bytes.Buffer
+ for pb.Next() {
+ // The loop body is executed b.N times total across all goroutines.
+ buf.Reset()
+ templ.Execute(&buf, "World")
+ }
+ })
+ })
+}
+
+func TestReportMetric(t *testing.T) {
+ res := testing.Benchmark(func(b *testing.B) {
+ b.ReportMetric(12345, "ns/op")
+ b.ReportMetric(0.2, "frobs/op")
+ })
+ // Test built-in overriding.
+ if res.NsPerOp() != 12345 {
+ t.Errorf("NsPerOp: expected %v, actual %v", 12345, res.NsPerOp())
+ }
+ // Test stringing.
+ res.N = 1 // Make the output stable
+ want := " 1\t 12345 ns/op\t 0.2000 frobs/op"
+ if want != res.String() {
+ t.Errorf("expected %q, actual %q", want, res.String())
+ }
+}
+
+func ExampleB_ReportMetric() {
+ // This reports a custom benchmark metric relevant to a
+ // specific algorithm (in this case, sorting).
+ testing.Benchmark(func(b *testing.B) {
+ var compares int64
+ for i := 0; i < b.N; i++ {
+ s := []int{5, 4, 3, 2, 1}
+ sort.Slice(s, func(i, j int) bool {
+ compares++
+ return s[i] < s[j]
+ })
+ }
+ // This metric is per-operation, so divide by b.N and
+ // report it as a "/op" unit.
+ b.ReportMetric(float64(compares)/float64(b.N), "compares/op")
+ // This metric is per-time, so divide by b.Elapsed and
+ // report it as a "/ns" unit.
+ b.ReportMetric(float64(compares)/float64(b.Elapsed().Nanoseconds()), "compares/ns")
+ })
+}
+
+func ExampleB_ReportMetric_parallel() {
+ // This reports a custom benchmark metric relevant to a
+ // specific algorithm (in this case, sorting) in parallel.
+ testing.Benchmark(func(b *testing.B) {
+ var compares atomic.Int64
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ s := []int{5, 4, 3, 2, 1}
+ sort.Slice(s, func(i, j int) bool {
+ // Because RunParallel runs the function many
+ // times in parallel, we must increment the
+ // counter atomically to avoid racing writes.
+ compares.Add(1)
+ return s[i] < s[j]
+ })
+ }
+ })
+
+ // NOTE: Report each metric once, after all of the parallel
+ // calls have completed.
+
+ // This metric is per-operation, so divide by b.N and
+ // report it as a "/op" unit.
+ b.ReportMetric(float64(compares.Load())/float64(b.N), "compares/op")
+ // This metric is per-time, so divide by b.Elapsed and
+ // report it as a "/ns" unit.
+ b.ReportMetric(float64(compares.Load())/float64(b.Elapsed().Nanoseconds()), "compares/ns")
+ })
+}
diff --git a/src/testing/cover.go b/src/testing/cover.go
new file mode 100644
index 0000000..6ad43ab
--- /dev/null
+++ b/src/testing/cover.go
@@ -0,0 +1,124 @@
+// Copyright 2013 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.
+
+// Support for test coverage.
+
+package testing
+
+import (
+ "fmt"
+ "internal/goexperiment"
+ "os"
+ "sync/atomic"
+)
+
+// CoverBlock records the coverage data for a single basic block.
+// The fields are 1-indexed, as in an editor: The opening line of
+// the file is number 1, for example. Columns are measured
+// in bytes.
+// NOTE: This struct is internal to the testing infrastructure and may change.
+// It is not covered (yet) by the Go 1 compatibility guidelines.
+type CoverBlock struct {
+ Line0 uint32 // Line number for block start.
+ Col0 uint16 // Column number for block start.
+ Line1 uint32 // Line number for block end.
+ Col1 uint16 // Column number for block end.
+ Stmts uint16 // Number of statements included in this block.
+}
+
+var cover Cover
+
+// Cover records information about test coverage checking.
+// NOTE: This struct is internal to the testing infrastructure and may change.
+// It is not covered (yet) by the Go 1 compatibility guidelines.
+type Cover struct {
+ Mode string
+ Counters map[string][]uint32
+ Blocks map[string][]CoverBlock
+ CoveredPackages string
+}
+
+// Coverage reports the current code coverage as a fraction in the range [0, 1].
+// If coverage is not enabled, Coverage returns 0.
+//
+// When running a large set of sequential test cases, checking Coverage after each one
+// can be useful for identifying which test cases exercise new code paths.
+// It is not a replacement for the reports generated by 'go test -cover' and
+// 'go tool cover'.
+func Coverage() float64 {
+ if goexperiment.CoverageRedesign {
+ return coverage2()
+ }
+ var n, d int64
+ for _, counters := range cover.Counters {
+ for i := range counters {
+ if atomic.LoadUint32(&counters[i]) > 0 {
+ n++
+ }
+ d++
+ }
+ }
+ if d == 0 {
+ return 0
+ }
+ return float64(n) / float64(d)
+}
+
+// RegisterCover records the coverage data accumulators for the tests.
+// NOTE: This function is internal to the testing infrastructure and may change.
+// It is not covered (yet) by the Go 1 compatibility guidelines.
+func RegisterCover(c Cover) {
+ cover = c
+}
+
+// mustBeNil checks the error and, if present, reports it and exits.
+func mustBeNil(err error) {
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "testing: %s\n", err)
+ os.Exit(2)
+ }
+}
+
+// coverReport reports the coverage percentage and writes a coverage profile if requested.
+func coverReport() {
+ if goexperiment.CoverageRedesign {
+ coverReport2()
+ return
+ }
+ var f *os.File
+ var err error
+ if *coverProfile != "" {
+ f, err = os.Create(toOutputDir(*coverProfile))
+ mustBeNil(err)
+ fmt.Fprintf(f, "mode: %s\n", cover.Mode)
+ defer func() { mustBeNil(f.Close()) }()
+ }
+
+ var active, total int64
+ var count uint32
+ for name, counts := range cover.Counters {
+ blocks := cover.Blocks[name]
+ for i := range counts {
+ stmts := int64(blocks[i].Stmts)
+ total += stmts
+ count = atomic.LoadUint32(&counts[i]) // For -mode=atomic.
+ if count > 0 {
+ active += stmts
+ }
+ if f != nil {
+ _, err := fmt.Fprintf(f, "%s:%d.%d,%d.%d %d %d\n", name,
+ blocks[i].Line0, blocks[i].Col0,
+ blocks[i].Line1, blocks[i].Col1,
+ stmts,
+ count)
+ mustBeNil(err)
+ }
+ }
+ }
+ if total == 0 {
+ fmt.Println("coverage: [no statements]")
+ return
+ }
+ fmt.Printf("coverage: %.1f%% of statements%s\n", 100*float64(active)/float64(total), cover.CoveredPackages)
+}
diff --git a/src/testing/example.go b/src/testing/example.go
new file mode 100644
index 0000000..07aa5cb
--- /dev/null
+++ b/src/testing/example.go
@@ -0,0 +1,97 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testing
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+ "time"
+)
+
+type InternalExample struct {
+ Name string
+ F func()
+ Output string
+ Unordered bool
+}
+
+// RunExamples is an internal function but exported because it is cross-package;
+// it is part of the implementation of the "go test" command.
+func RunExamples(matchString func(pat, str string) (bool, error), examples []InternalExample) (ok bool) {
+ _, ok = runExamples(matchString, examples)
+ return ok
+}
+
+func runExamples(matchString func(pat, str string) (bool, error), examples []InternalExample) (ran, ok bool) {
+ ok = true
+
+ m := newMatcher(matchString, *match, "-test.run", *skip)
+
+ var eg InternalExample
+ for _, eg = range examples {
+ _, matched, _ := m.fullName(nil, eg.Name)
+ if !matched {
+ continue
+ }
+ ran = true
+ if !runExample(eg) {
+ ok = false
+ }
+ }
+
+ return ran, ok
+}
+
+func sortLines(output string) string {
+ lines := strings.Split(output, "\n")
+ sort.Strings(lines)
+ return strings.Join(lines, "\n")
+}
+
+// processRunResult computes a summary and status of the result of running an example test.
+// stdout is the captured output from stdout of the test.
+// recovered is the result of invoking recover after running the test, in case it panicked.
+//
+// If stdout doesn't match the expected output or if recovered is non-nil, it'll print the cause of failure to stdout.
+// If the test is chatty/verbose, it'll print a success message to stdout.
+// If recovered is non-nil, it'll panic with that value.
+// If the test panicked with nil, or invoked runtime.Goexit, it'll be
+// made to fail and panic with errNilPanicOrGoexit
+func (eg *InternalExample) processRunResult(stdout string, timeSpent time.Duration, finished bool, recovered any) (passed bool) {
+ passed = true
+ dstr := fmtDuration(timeSpent)
+ var fail string
+ got := strings.TrimSpace(stdout)
+ want := strings.TrimSpace(eg.Output)
+ if eg.Unordered {
+ if sortLines(got) != sortLines(want) && recovered == nil {
+ fail = fmt.Sprintf("got:\n%s\nwant (unordered):\n%s\n", stdout, eg.Output)
+ }
+ } else {
+ if got != want && recovered == nil {
+ fail = fmt.Sprintf("got:\n%s\nwant:\n%s\n", got, want)
+ }
+ }
+ if fail != "" || !finished || recovered != nil {
+ fmt.Printf("%s--- FAIL: %s (%s)\n%s", chatty.prefix(), eg.Name, dstr, fail)
+ passed = false
+ } else if chatty.on {
+ fmt.Printf("%s--- PASS: %s (%s)\n", chatty.prefix(), eg.Name, dstr)
+ }
+
+ if chatty.on && chatty.json {
+ fmt.Printf("%s=== NAME %s\n", chatty.prefix(), "")
+ }
+
+ if recovered != nil {
+ // Propagate the previously recovered result, by panicking.
+ panic(recovered)
+ } else if !finished {
+ panic(errNilPanicOrGoexit)
+ }
+
+ return
+}
diff --git a/src/testing/export_test.go b/src/testing/export_test.go
new file mode 100644
index 0000000..0022491
--- /dev/null
+++ b/src/testing/export_test.go
@@ -0,0 +1,7 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testing
+
+var PrettyPrint = prettyPrint
diff --git a/src/testing/flag_test.go b/src/testing/flag_test.go
new file mode 100644
index 0000000..6f76c23
--- /dev/null
+++ b/src/testing/flag_test.go
@@ -0,0 +1,89 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testing_test
+
+import (
+ "flag"
+ "internal/testenv"
+ "os"
+ "os/exec"
+ "testing"
+)
+
+var testFlagArg = flag.String("test_flag_arg", "", "TestFlag: passing -v option")
+
+const flagTestEnv = "GO_WANT_FLAG_HELPER_PROCESS"
+
+func TestFlag(t *testing.T) {
+ if os.Getenv(flagTestEnv) == "1" {
+ testFlagHelper(t)
+ return
+ }
+
+ testenv.MustHaveExec(t)
+
+ for _, flag := range []string{"", "-test.v", "-test.v=test2json"} {
+ flag := flag
+ t.Run(flag, func(t *testing.T) {
+ t.Parallel()
+ exe, err := os.Executable()
+ if err != nil {
+ exe = os.Args[0]
+ }
+ cmd := exec.Command(exe, "-test.run=^TestFlag$", "-test_flag_arg="+flag)
+ if flag != "" {
+ cmd.Args = append(cmd.Args, flag)
+ }
+ cmd.Env = append(cmd.Environ(), flagTestEnv+"=1")
+ b, err := cmd.CombinedOutput()
+ if len(b) > 0 {
+ // When we set -test.v=test2json, we need to escape the ^V control
+ // character used for JSON framing so that the JSON parser doesn't
+ // misinterpret the subprocess output as output from the parent test.
+ t.Logf("%q", b)
+ }
+ if err != nil {
+ t.Error(err)
+ }
+ })
+ }
+}
+
+// testFlagHelper is called by the TestFlagHelper subprocess.
+func testFlagHelper(t *testing.T) {
+ f := flag.Lookup("test.v")
+ if f == nil {
+ t.Fatal(`flag.Lookup("test.v") failed`)
+ }
+
+ bf, ok := f.Value.(interface{ IsBoolFlag() bool })
+ if !ok {
+ t.Errorf("test.v flag (type %T) does not have IsBoolFlag method", f)
+ } else if !bf.IsBoolFlag() {
+ t.Error("test.v IsBoolFlag() returned false")
+ }
+
+ gf, ok := f.Value.(flag.Getter)
+ if !ok {
+ t.Fatalf("test.v flag (type %T) does not have Get method", f)
+ }
+ v := gf.Get()
+
+ var want any
+ switch *testFlagArg {
+ case "":
+ want = false
+ case "-test.v":
+ want = true
+ case "-test.v=test2json":
+ want = "test2json"
+ default:
+ t.Fatalf("unexpected test_flag_arg %q", *testFlagArg)
+ }
+
+ if v != want {
+ t.Errorf("test.v is %v want %v", v, want)
+ }
+}
diff --git a/src/testing/fstest/mapfs.go b/src/testing/fstest/mapfs.go
new file mode 100644
index 0000000..1409d62
--- /dev/null
+++ b/src/testing/fstest/mapfs.go
@@ -0,0 +1,244 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package fstest
+
+import (
+ "io"
+ "io/fs"
+ "path"
+ "sort"
+ "strings"
+ "time"
+)
+
+// A MapFS is a simple in-memory file system for use in tests,
+// represented as a map from path names (arguments to Open)
+// to information about the files or directories they represent.
+//
+// The map need not include parent directories for files contained
+// in the map; those will be synthesized if needed.
+// But a directory can still be included by setting the [MapFile.Mode]'s [fs.ModeDir] bit;
+// this may be necessary for detailed control over the directory's [fs.FileInfo]
+// or to create an empty directory.
+//
+// File system operations read directly from the map,
+// so that the file system can be changed by editing the map as needed.
+// An implication is that file system operations must not run concurrently
+// with changes to the map, which would be a race.
+// Another implication is that opening or reading a directory requires
+// iterating over the entire map, so a MapFS should typically be used with not more
+// than a few hundred entries or directory reads.
+type MapFS map[string]*MapFile
+
+// A MapFile describes a single file in a [MapFS].
+type MapFile struct {
+ Data []byte // file content
+ Mode fs.FileMode // fs.FileInfo.Mode
+ ModTime time.Time // fs.FileInfo.ModTime
+ Sys any // fs.FileInfo.Sys
+}
+
+var _ fs.FS = MapFS(nil)
+var _ fs.File = (*openMapFile)(nil)
+
+// Open opens the named file.
+func (fsys MapFS) Open(name string) (fs.File, error) {
+ if !fs.ValidPath(name) {
+ return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
+ }
+ file := fsys[name]
+ if file != nil && file.Mode&fs.ModeDir == 0 {
+ // Ordinary file
+ return &openMapFile{name, mapFileInfo{path.Base(name), file}, 0}, nil
+ }
+
+ // Directory, possibly synthesized.
+ // Note that file can be nil here: the map need not contain explicit parent directories for all its files.
+ // But file can also be non-nil, in case the user wants to set metadata for the directory explicitly.
+ // Either way, we need to construct the list of children of this directory.
+ var list []mapFileInfo
+ var elem string
+ var need = make(map[string]bool)
+ if name == "." {
+ elem = "."
+ for fname, f := range fsys {
+ i := strings.Index(fname, "/")
+ if i < 0 {
+ if fname != "." {
+ list = append(list, mapFileInfo{fname, f})
+ }
+ } else {
+ need[fname[:i]] = true
+ }
+ }
+ } else {
+ elem = name[strings.LastIndex(name, "/")+1:]
+ prefix := name + "/"
+ for fname, f := range fsys {
+ if strings.HasPrefix(fname, prefix) {
+ felem := fname[len(prefix):]
+ i := strings.Index(felem, "/")
+ if i < 0 {
+ list = append(list, mapFileInfo{felem, f})
+ } else {
+ need[fname[len(prefix):len(prefix)+i]] = true
+ }
+ }
+ }
+ // If the directory name is not in the map,
+ // and there are no children of the name in the map,
+ // then the directory is treated as not existing.
+ if file == nil && list == nil && len(need) == 0 {
+ return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
+ }
+ }
+ for _, fi := range list {
+ delete(need, fi.name)
+ }
+ for name := range need {
+ list = append(list, mapFileInfo{name, &MapFile{Mode: fs.ModeDir | 0555}})
+ }
+ sort.Slice(list, func(i, j int) bool {
+ return list[i].name < list[j].name
+ })
+
+ if file == nil {
+ file = &MapFile{Mode: fs.ModeDir | 0555}
+ }
+ return &mapDir{name, mapFileInfo{elem, file}, list, 0}, nil
+}
+
+// fsOnly is a wrapper that hides all but the fs.FS methods,
+// to avoid an infinite recursion when implementing special
+// methods in terms of helpers that would use them.
+// (In general, implementing these methods using the package fs helpers
+// is redundant and unnecessary, but having the methods may make
+// MapFS exercise more code paths when used in tests.)
+type fsOnly struct{ fs.FS }
+
+func (fsys MapFS) ReadFile(name string) ([]byte, error) {
+ return fs.ReadFile(fsOnly{fsys}, name)
+}
+
+func (fsys MapFS) Stat(name string) (fs.FileInfo, error) {
+ return fs.Stat(fsOnly{fsys}, name)
+}
+
+func (fsys MapFS) ReadDir(name string) ([]fs.DirEntry, error) {
+ return fs.ReadDir(fsOnly{fsys}, name)
+}
+
+func (fsys MapFS) Glob(pattern string) ([]string, error) {
+ return fs.Glob(fsOnly{fsys}, pattern)
+}
+
+type noSub struct {
+ MapFS
+}
+
+func (noSub) Sub() {} // not the fs.SubFS signature
+
+func (fsys MapFS) Sub(dir string) (fs.FS, error) {
+ return fs.Sub(noSub{fsys}, dir)
+}
+
+// A mapFileInfo implements fs.FileInfo and fs.DirEntry for a given map file.
+type mapFileInfo struct {
+ name string
+ f *MapFile
+}
+
+func (i *mapFileInfo) Name() string { return i.name }
+func (i *mapFileInfo) Size() int64 { return int64(len(i.f.Data)) }
+func (i *mapFileInfo) Mode() fs.FileMode { return i.f.Mode }
+func (i *mapFileInfo) Type() fs.FileMode { return i.f.Mode.Type() }
+func (i *mapFileInfo) ModTime() time.Time { return i.f.ModTime }
+func (i *mapFileInfo) IsDir() bool { return i.f.Mode&fs.ModeDir != 0 }
+func (i *mapFileInfo) Sys() any { return i.f.Sys }
+func (i *mapFileInfo) Info() (fs.FileInfo, error) { return i, nil }
+
+func (i *mapFileInfo) String() string {
+ return fs.FormatFileInfo(i)
+}
+
+// An openMapFile is a regular (non-directory) fs.File open for reading.
+type openMapFile struct {
+ path string
+ mapFileInfo
+ offset int64
+}
+
+func (f *openMapFile) Stat() (fs.FileInfo, error) { return &f.mapFileInfo, nil }
+
+func (f *openMapFile) Close() error { return nil }
+
+func (f *openMapFile) Read(b []byte) (int, error) {
+ if f.offset >= int64(len(f.f.Data)) {
+ return 0, io.EOF
+ }
+ if f.offset < 0 {
+ return 0, &fs.PathError{Op: "read", Path: f.path, Err: fs.ErrInvalid}
+ }
+ n := copy(b, f.f.Data[f.offset:])
+ f.offset += int64(n)
+ return n, nil
+}
+
+func (f *openMapFile) Seek(offset int64, whence int) (int64, error) {
+ switch whence {
+ case 0:
+ // offset += 0
+ case 1:
+ offset += f.offset
+ case 2:
+ offset += int64(len(f.f.Data))
+ }
+ if offset < 0 || offset > int64(len(f.f.Data)) {
+ return 0, &fs.PathError{Op: "seek", Path: f.path, Err: fs.ErrInvalid}
+ }
+ f.offset = offset
+ return offset, nil
+}
+
+func (f *openMapFile) ReadAt(b []byte, offset int64) (int, error) {
+ if offset < 0 || offset > int64(len(f.f.Data)) {
+ return 0, &fs.PathError{Op: "read", Path: f.path, Err: fs.ErrInvalid}
+ }
+ n := copy(b, f.f.Data[offset:])
+ if n < len(b) {
+ return n, io.EOF
+ }
+ return n, nil
+}
+
+// A mapDir is a directory fs.File (so also an fs.ReadDirFile) open for reading.
+type mapDir struct {
+ path string
+ mapFileInfo
+ entry []mapFileInfo
+ offset int
+}
+
+func (d *mapDir) Stat() (fs.FileInfo, error) { return &d.mapFileInfo, nil }
+func (d *mapDir) Close() error { return nil }
+func (d *mapDir) Read(b []byte) (int, error) {
+ return 0, &fs.PathError{Op: "read", Path: d.path, Err: fs.ErrInvalid}
+}
+
+func (d *mapDir) ReadDir(count int) ([]fs.DirEntry, error) {
+ n := len(d.entry) - d.offset
+ if n == 0 && count > 0 {
+ return nil, io.EOF
+ }
+ if count > 0 && n > count {
+ n = count
+ }
+ list := make([]fs.DirEntry, n)
+ for i := range list {
+ list[i] = &d.entry[d.offset+i]
+ }
+ d.offset += n
+ return list, nil
+}
diff --git a/src/testing/fstest/mapfs_test.go b/src/testing/fstest/mapfs_test.go
new file mode 100644
index 0000000..c64dc8d
--- /dev/null
+++ b/src/testing/fstest/mapfs_test.go
@@ -0,0 +1,47 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package fstest
+
+import (
+ "fmt"
+ "io/fs"
+ "strings"
+ "testing"
+)
+
+func TestMapFS(t *testing.T) {
+ m := MapFS{
+ "hello": {Data: []byte("hello, world\n")},
+ "fortune/k/ken.txt": {Data: []byte("If a program is too slow, it must have a loop.\n")},
+ }
+ if err := TestFS(m, "hello", "fortune", "fortune/k", "fortune/k/ken.txt"); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestMapFSChmodDot(t *testing.T) {
+ m := MapFS{
+ "a/b.txt": &MapFile{Mode: 0666},
+ ".": &MapFile{Mode: 0777 | fs.ModeDir},
+ }
+ buf := new(strings.Builder)
+ fs.WalkDir(m, ".", func(path string, d fs.DirEntry, err error) error {
+ fi, err := d.Info()
+ if err != nil {
+ return err
+ }
+ fmt.Fprintf(buf, "%s: %v\n", path, fi.Mode())
+ return nil
+ })
+ want := `
+.: drwxrwxrwx
+a: dr-xr-xr-x
+a/b.txt: -rw-rw-rw-
+`[1:]
+ got := buf.String()
+ if want != got {
+ t.Errorf("MapFS modes want:\n%s\ngot:\n%s\n", want, got)
+ }
+}
diff --git a/src/testing/fstest/testfs.go b/src/testing/fstest/testfs.go
new file mode 100644
index 0000000..78b0b82
--- /dev/null
+++ b/src/testing/fstest/testfs.go
@@ -0,0 +1,624 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package fstest implements support for testing implementations and users of file systems.
+package fstest
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "io/fs"
+ "path"
+ "reflect"
+ "sort"
+ "strings"
+ "testing/iotest"
+)
+
+// TestFS tests a file system implementation.
+// It walks the entire tree of files in fsys,
+// opening and checking that each file behaves correctly.
+// It also checks that the file system contains at least the expected files.
+// As a special case, if no expected files are listed, fsys must be empty.
+// Otherwise, fsys must contain at least the listed files; it can also contain others.
+// The contents of fsys must not change concurrently with TestFS.
+//
+// If TestFS finds any misbehaviors, it returns an error reporting all of them.
+// The error text spans multiple lines, one per detected misbehavior.
+//
+// Typical usage inside a test is:
+//
+// if err := fstest.TestFS(myFS, "file/that/should/be/present"); err != nil {
+// t.Fatal(err)
+// }
+func TestFS(fsys fs.FS, expected ...string) error {
+ if err := testFS(fsys, expected...); err != nil {
+ return err
+ }
+ for _, name := range expected {
+ if i := strings.Index(name, "/"); i >= 0 {
+ dir, dirSlash := name[:i], name[:i+1]
+ var subExpected []string
+ for _, name := range expected {
+ if strings.HasPrefix(name, dirSlash) {
+ subExpected = append(subExpected, name[len(dirSlash):])
+ }
+ }
+ sub, err := fs.Sub(fsys, dir)
+ if err != nil {
+ return err
+ }
+ if err := testFS(sub, subExpected...); err != nil {
+ return fmt.Errorf("testing fs.Sub(fsys, %s): %v", dir, err)
+ }
+ break // one sub-test is enough
+ }
+ }
+ return nil
+}
+
+func testFS(fsys fs.FS, expected ...string) error {
+ t := fsTester{fsys: fsys}
+ t.checkDir(".")
+ t.checkOpen(".")
+ found := make(map[string]bool)
+ for _, dir := range t.dirs {
+ found[dir] = true
+ }
+ for _, file := range t.files {
+ found[file] = true
+ }
+ delete(found, ".")
+ if len(expected) == 0 && len(found) > 0 {
+ var list []string
+ for k := range found {
+ if k != "." {
+ list = append(list, k)
+ }
+ }
+ sort.Strings(list)
+ if len(list) > 15 {
+ list = append(list[:10], "...")
+ }
+ t.errorf("expected empty file system but found files:\n%s", strings.Join(list, "\n"))
+ }
+ for _, name := range expected {
+ if !found[name] {
+ t.errorf("expected but not found: %s", name)
+ }
+ }
+ if len(t.errText) == 0 {
+ return nil
+ }
+ return errors.New("TestFS found errors:\n" + string(t.errText))
+}
+
+// An fsTester holds state for running the test.
+type fsTester struct {
+ fsys fs.FS
+ errText []byte
+ dirs []string
+ files []string
+}
+
+// errorf adds an error line to errText.
+func (t *fsTester) errorf(format string, args ...any) {
+ if len(t.errText) > 0 {
+ t.errText = append(t.errText, '\n')
+ }
+ t.errText = append(t.errText, fmt.Sprintf(format, args...)...)
+}
+
+func (t *fsTester) openDir(dir string) fs.ReadDirFile {
+ f, err := t.fsys.Open(dir)
+ if err != nil {
+ t.errorf("%s: Open: %v", dir, err)
+ return nil
+ }
+ d, ok := f.(fs.ReadDirFile)
+ if !ok {
+ f.Close()
+ t.errorf("%s: Open returned File type %T, not a fs.ReadDirFile", dir, f)
+ return nil
+ }
+ return d
+}
+
+// checkDir checks the directory dir, which is expected to exist
+// (it is either the root or was found in a directory listing with IsDir true).
+func (t *fsTester) checkDir(dir string) {
+ // Read entire directory.
+ t.dirs = append(t.dirs, dir)
+ d := t.openDir(dir)
+ if d == nil {
+ return
+ }
+ list, err := d.ReadDir(-1)
+ if err != nil {
+ d.Close()
+ t.errorf("%s: ReadDir(-1): %v", dir, err)
+ return
+ }
+
+ // Check all children.
+ var prefix string
+ if dir == "." {
+ prefix = ""
+ } else {
+ prefix = dir + "/"
+ }
+ for _, info := range list {
+ name := info.Name()
+ switch {
+ case name == ".", name == "..", name == "":
+ t.errorf("%s: ReadDir: child has invalid name: %#q", dir, name)
+ continue
+ case strings.Contains(name, "/"):
+ t.errorf("%s: ReadDir: child name contains slash: %#q", dir, name)
+ continue
+ case strings.Contains(name, `\`):
+ t.errorf("%s: ReadDir: child name contains backslash: %#q", dir, name)
+ continue
+ }
+ path := prefix + name
+ t.checkStat(path, info)
+ t.checkOpen(path)
+ if info.IsDir() {
+ t.checkDir(path)
+ } else {
+ t.checkFile(path)
+ }
+ }
+
+ // Check ReadDir(-1) at EOF.
+ list2, err := d.ReadDir(-1)
+ if len(list2) > 0 || err != nil {
+ d.Close()
+ t.errorf("%s: ReadDir(-1) at EOF = %d entries, %v, wanted 0 entries, nil", dir, len(list2), err)
+ return
+ }
+
+ // Check ReadDir(1) at EOF (different results).
+ list2, err = d.ReadDir(1)
+ if len(list2) > 0 || err != io.EOF {
+ d.Close()
+ t.errorf("%s: ReadDir(1) at EOF = %d entries, %v, wanted 0 entries, EOF", dir, len(list2), err)
+ return
+ }
+
+ // Check that close does not report an error.
+ if err := d.Close(); err != nil {
+ t.errorf("%s: Close: %v", dir, err)
+ }
+
+ // Check that closing twice doesn't crash.
+ // The return value doesn't matter.
+ d.Close()
+
+ // Reopen directory, read a second time, make sure contents match.
+ if d = t.openDir(dir); d == nil {
+ return
+ }
+ defer d.Close()
+ list2, err = d.ReadDir(-1)
+ if err != nil {
+ t.errorf("%s: second Open+ReadDir(-1): %v", dir, err)
+ return
+ }
+ t.checkDirList(dir, "first Open+ReadDir(-1) vs second Open+ReadDir(-1)", list, list2)
+
+ // Reopen directory, read a third time in pieces, make sure contents match.
+ if d = t.openDir(dir); d == nil {
+ return
+ }
+ defer d.Close()
+ list2 = nil
+ for {
+ n := 1
+ if len(list2) > 0 {
+ n = 2
+ }
+ frag, err := d.ReadDir(n)
+ if len(frag) > n {
+ t.errorf("%s: third Open: ReadDir(%d) after %d: %d entries (too many)", dir, n, len(list2), len(frag))
+ return
+ }
+ list2 = append(list2, frag...)
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ t.errorf("%s: third Open: ReadDir(%d) after %d: %v", dir, n, len(list2), err)
+ return
+ }
+ if n == 0 {
+ t.errorf("%s: third Open: ReadDir(%d) after %d: 0 entries but nil error", dir, n, len(list2))
+ return
+ }
+ }
+ t.checkDirList(dir, "first Open+ReadDir(-1) vs third Open+ReadDir(1,2) loop", list, list2)
+
+ // If fsys has ReadDir, check that it matches and is sorted.
+ if fsys, ok := t.fsys.(fs.ReadDirFS); ok {
+ list2, err := fsys.ReadDir(dir)
+ if err != nil {
+ t.errorf("%s: fsys.ReadDir: %v", dir, err)
+ return
+ }
+ t.checkDirList(dir, "first Open+ReadDir(-1) vs fsys.ReadDir", list, list2)
+
+ for i := 0; i+1 < len(list2); i++ {
+ if list2[i].Name() >= list2[i+1].Name() {
+ t.errorf("%s: fsys.ReadDir: list not sorted: %s before %s", dir, list2[i].Name(), list2[i+1].Name())
+ }
+ }
+ }
+
+ // Check fs.ReadDir as well.
+ list2, err = fs.ReadDir(t.fsys, dir)
+ if err != nil {
+ t.errorf("%s: fs.ReadDir: %v", dir, err)
+ return
+ }
+ t.checkDirList(dir, "first Open+ReadDir(-1) vs fs.ReadDir", list, list2)
+
+ for i := 0; i+1 < len(list2); i++ {
+ if list2[i].Name() >= list2[i+1].Name() {
+ t.errorf("%s: fs.ReadDir: list not sorted: %s before %s", dir, list2[i].Name(), list2[i+1].Name())
+ }
+ }
+
+ t.checkGlob(dir, list2)
+}
+
+// formatEntry formats an fs.DirEntry into a string for error messages and comparison.
+func formatEntry(entry fs.DirEntry) string {
+ return fmt.Sprintf("%s IsDir=%v Type=%v", entry.Name(), entry.IsDir(), entry.Type())
+}
+
+// formatInfoEntry formats an fs.FileInfo into a string like the result of formatEntry, for error messages and comparison.
+func formatInfoEntry(info fs.FileInfo) string {
+ return fmt.Sprintf("%s IsDir=%v Type=%v", info.Name(), info.IsDir(), info.Mode().Type())
+}
+
+// formatInfo formats an fs.FileInfo into a string for error messages and comparison.
+func formatInfo(info fs.FileInfo) string {
+ return fmt.Sprintf("%s IsDir=%v Mode=%v Size=%d ModTime=%v", info.Name(), info.IsDir(), info.Mode(), info.Size(), info.ModTime())
+}
+
+// checkGlob checks that various glob patterns work if the file system implements GlobFS.
+func (t *fsTester) checkGlob(dir string, list []fs.DirEntry) {
+ if _, ok := t.fsys.(fs.GlobFS); !ok {
+ return
+ }
+
+ // Make a complex glob pattern prefix that only matches dir.
+ var glob string
+ if dir != "." {
+ elem := strings.Split(dir, "/")
+ for i, e := range elem {
+ var pattern []rune
+ for j, r := range e {
+ if r == '*' || r == '?' || r == '\\' || r == '[' || r == '-' {
+ pattern = append(pattern, '\\', r)
+ continue
+ }
+ switch (i + j) % 5 {
+ case 0:
+ pattern = append(pattern, r)
+ case 1:
+ pattern = append(pattern, '[', r, ']')
+ case 2:
+ pattern = append(pattern, '[', r, '-', r, ']')
+ case 3:
+ pattern = append(pattern, '[', '\\', r, ']')
+ case 4:
+ pattern = append(pattern, '[', '\\', r, '-', '\\', r, ']')
+ }
+ }
+ elem[i] = string(pattern)
+ }
+ glob = strings.Join(elem, "/") + "/"
+ }
+
+ // Test that malformed patterns are detected.
+ // The error is likely path.ErrBadPattern but need not be.
+ if _, err := t.fsys.(fs.GlobFS).Glob(glob + "nonexist/[]"); err == nil {
+ t.errorf("%s: Glob(%#q): bad pattern not detected", dir, glob+"nonexist/[]")
+ }
+
+ // Try to find a letter that appears in only some of the final names.
+ c := rune('a')
+ for ; c <= 'z'; c++ {
+ have, haveNot := false, false
+ for _, d := range list {
+ if strings.ContainsRune(d.Name(), c) {
+ have = true
+ } else {
+ haveNot = true
+ }
+ }
+ if have && haveNot {
+ break
+ }
+ }
+ if c > 'z' {
+ c = 'a'
+ }
+ glob += "*" + string(c) + "*"
+
+ var want []string
+ for _, d := range list {
+ if strings.ContainsRune(d.Name(), c) {
+ want = append(want, path.Join(dir, d.Name()))
+ }
+ }
+
+ names, err := t.fsys.(fs.GlobFS).Glob(glob)
+ if err != nil {
+ t.errorf("%s: Glob(%#q): %v", dir, glob, err)
+ return
+ }
+ if reflect.DeepEqual(want, names) {
+ return
+ }
+
+ if !sort.StringsAreSorted(names) {
+ t.errorf("%s: Glob(%#q): unsorted output:\n%s", dir, glob, strings.Join(names, "\n"))
+ sort.Strings(names)
+ }
+
+ var problems []string
+ for len(want) > 0 || len(names) > 0 {
+ switch {
+ case len(want) > 0 && len(names) > 0 && want[0] == names[0]:
+ want, names = want[1:], names[1:]
+ case len(want) > 0 && (len(names) == 0 || want[0] < names[0]):
+ problems = append(problems, "missing: "+want[0])
+ want = want[1:]
+ default:
+ problems = append(problems, "extra: "+names[0])
+ names = names[1:]
+ }
+ }
+ t.errorf("%s: Glob(%#q): wrong output:\n%s", dir, glob, strings.Join(problems, "\n"))
+}
+
+// checkStat checks that a direct stat of path matches entry,
+// which was found in the parent's directory listing.
+func (t *fsTester) checkStat(path string, entry fs.DirEntry) {
+ file, err := t.fsys.Open(path)
+ if err != nil {
+ t.errorf("%s: Open: %v", path, err)
+ return
+ }
+ info, err := file.Stat()
+ file.Close()
+ if err != nil {
+ t.errorf("%s: Stat: %v", path, err)
+ return
+ }
+ fentry := formatEntry(entry)
+ fientry := formatInfoEntry(info)
+ // Note: mismatch here is OK for symlink, because Open dereferences symlink.
+ if fentry != fientry && entry.Type()&fs.ModeSymlink == 0 {
+ t.errorf("%s: mismatch:\n\tentry = %s\n\tfile.Stat() = %s", path, fentry, fientry)
+ }
+
+ einfo, err := entry.Info()
+ if err != nil {
+ t.errorf("%s: entry.Info: %v", path, err)
+ return
+ }
+ finfo := formatInfo(info)
+ if entry.Type()&fs.ModeSymlink != 0 {
+ // For symlink, just check that entry.Info matches entry on common fields.
+ // Open deferences symlink, so info itself may differ.
+ feentry := formatInfoEntry(einfo)
+ if fentry != feentry {
+ t.errorf("%s: mismatch\n\tentry = %s\n\tentry.Info() = %s\n", path, fentry, feentry)
+ }
+ } else {
+ feinfo := formatInfo(einfo)
+ if feinfo != finfo {
+ t.errorf("%s: mismatch:\n\tentry.Info() = %s\n\tfile.Stat() = %s\n", path, feinfo, finfo)
+ }
+ }
+
+ // Stat should be the same as Open+Stat, even for symlinks.
+ info2, err := fs.Stat(t.fsys, path)
+ if err != nil {
+ t.errorf("%s: fs.Stat: %v", path, err)
+ return
+ }
+ finfo2 := formatInfo(info2)
+ if finfo2 != finfo {
+ t.errorf("%s: fs.Stat(...) = %s\n\twant %s", path, finfo2, finfo)
+ }
+
+ if fsys, ok := t.fsys.(fs.StatFS); ok {
+ info2, err := fsys.Stat(path)
+ if err != nil {
+ t.errorf("%s: fsys.Stat: %v", path, err)
+ return
+ }
+ finfo2 := formatInfo(info2)
+ if finfo2 != finfo {
+ t.errorf("%s: fsys.Stat(...) = %s\n\twant %s", path, finfo2, finfo)
+ }
+ }
+}
+
+// checkDirList checks that two directory lists contain the same files and file info.
+// The order of the lists need not match.
+func (t *fsTester) checkDirList(dir, desc string, list1, list2 []fs.DirEntry) {
+ old := make(map[string]fs.DirEntry)
+ checkMode := func(entry fs.DirEntry) {
+ if entry.IsDir() != (entry.Type()&fs.ModeDir != 0) {
+ if entry.IsDir() {
+ t.errorf("%s: ReadDir returned %s with IsDir() = true, Type() & ModeDir = 0", dir, entry.Name())
+ } else {
+ t.errorf("%s: ReadDir returned %s with IsDir() = false, Type() & ModeDir = ModeDir", dir, entry.Name())
+ }
+ }
+ }
+
+ for _, entry1 := range list1 {
+ old[entry1.Name()] = entry1
+ checkMode(entry1)
+ }
+
+ var diffs []string
+ for _, entry2 := range list2 {
+ entry1 := old[entry2.Name()]
+ if entry1 == nil {
+ checkMode(entry2)
+ diffs = append(diffs, "+ "+formatEntry(entry2))
+ continue
+ }
+ if formatEntry(entry1) != formatEntry(entry2) {
+ diffs = append(diffs, "- "+formatEntry(entry1), "+ "+formatEntry(entry2))
+ }
+ delete(old, entry2.Name())
+ }
+ for _, entry1 := range old {
+ diffs = append(diffs, "- "+formatEntry(entry1))
+ }
+
+ if len(diffs) == 0 {
+ return
+ }
+
+ sort.Slice(diffs, func(i, j int) bool {
+ fi := strings.Fields(diffs[i])
+ fj := strings.Fields(diffs[j])
+ // sort by name (i < j) and then +/- (j < i, because + < -)
+ return fi[1]+" "+fj[0] < fj[1]+" "+fi[0]
+ })
+
+ t.errorf("%s: diff %s:\n\t%s", dir, desc, strings.Join(diffs, "\n\t"))
+}
+
+// checkFile checks that basic file reading works correctly.
+func (t *fsTester) checkFile(file string) {
+ t.files = append(t.files, file)
+
+ // Read entire file.
+ f, err := t.fsys.Open(file)
+ if err != nil {
+ t.errorf("%s: Open: %v", file, err)
+ return
+ }
+
+ data, err := io.ReadAll(f)
+ if err != nil {
+ f.Close()
+ t.errorf("%s: Open+ReadAll: %v", file, err)
+ return
+ }
+
+ if err := f.Close(); err != nil {
+ t.errorf("%s: Close: %v", file, err)
+ }
+
+ // Check that closing twice doesn't crash.
+ // The return value doesn't matter.
+ f.Close()
+
+ // Check that ReadFile works if present.
+ if fsys, ok := t.fsys.(fs.ReadFileFS); ok {
+ data2, err := fsys.ReadFile(file)
+ if err != nil {
+ t.errorf("%s: fsys.ReadFile: %v", file, err)
+ return
+ }
+ t.checkFileRead(file, "ReadAll vs fsys.ReadFile", data, data2)
+
+ // Modify the data and check it again. Modifying the
+ // returned byte slice should not affect the next call.
+ for i := range data2 {
+ data2[i]++
+ }
+ data2, err = fsys.ReadFile(file)
+ if err != nil {
+ t.errorf("%s: second call to fsys.ReadFile: %v", file, err)
+ return
+ }
+ t.checkFileRead(file, "Readall vs second fsys.ReadFile", data, data2)
+
+ t.checkBadPath(file, "ReadFile",
+ func(name string) error { _, err := fsys.ReadFile(name); return err })
+ }
+
+ // Check that fs.ReadFile works with t.fsys.
+ data2, err := fs.ReadFile(t.fsys, file)
+ if err != nil {
+ t.errorf("%s: fs.ReadFile: %v", file, err)
+ return
+ }
+ t.checkFileRead(file, "ReadAll vs fs.ReadFile", data, data2)
+
+ // Use iotest.TestReader to check small reads, Seek, ReadAt.
+ f, err = t.fsys.Open(file)
+ if err != nil {
+ t.errorf("%s: second Open: %v", file, err)
+ return
+ }
+ defer f.Close()
+ if err := iotest.TestReader(f, data); err != nil {
+ t.errorf("%s: failed TestReader:\n\t%s", file, strings.ReplaceAll(err.Error(), "\n", "\n\t"))
+ }
+}
+
+func (t *fsTester) checkFileRead(file, desc string, data1, data2 []byte) {
+ if string(data1) != string(data2) {
+ t.errorf("%s: %s: different data returned\n\t%q\n\t%q", file, desc, data1, data2)
+ return
+ }
+}
+
+// checkBadPath checks that various invalid forms of file's name cannot be opened using t.fsys.Open.
+func (t *fsTester) checkOpen(file string) {
+ t.checkBadPath(file, "Open", func(file string) error {
+ f, err := t.fsys.Open(file)
+ if err == nil {
+ f.Close()
+ }
+ return err
+ })
+}
+
+// checkBadPath checks that various invalid forms of file's name cannot be opened using open.
+func (t *fsTester) checkBadPath(file string, desc string, open func(string) error) {
+ bad := []string{
+ "/" + file,
+ file + "/.",
+ }
+ if file == "." {
+ bad = append(bad, "/")
+ }
+ if i := strings.Index(file, "/"); i >= 0 {
+ bad = append(bad,
+ file[:i]+"//"+file[i+1:],
+ file[:i]+"/./"+file[i+1:],
+ file[:i]+`\`+file[i+1:],
+ file[:i]+"/../"+file,
+ )
+ }
+ if i := strings.LastIndex(file, "/"); i >= 0 {
+ bad = append(bad,
+ file[:i]+"//"+file[i+1:],
+ file[:i]+"/./"+file[i+1:],
+ file[:i]+`\`+file[i+1:],
+ file+"/../"+file[i+1:],
+ )
+ }
+
+ for _, b := range bad {
+ if err := open(b); err == nil {
+ t.errorf("%s: %s(%s) succeeded, want error", file, desc, b)
+ }
+ }
+}
diff --git a/src/testing/fstest/testfs_test.go b/src/testing/fstest/testfs_test.go
new file mode 100644
index 0000000..a48c597
--- /dev/null
+++ b/src/testing/fstest/testfs_test.go
@@ -0,0 +1,78 @@
+// Copyright 2021 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 fstest
+
+import (
+ "internal/testenv"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "sort"
+ "testing"
+)
+
+func TestSymlink(t *testing.T) {
+ testenv.MustHaveSymlink(t)
+
+ tmp := t.TempDir()
+ tmpfs := os.DirFS(tmp)
+
+ if err := os.WriteFile(filepath.Join(tmp, "hello"), []byte("hello, world\n"), 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := os.Symlink(filepath.Join(tmp, "hello"), filepath.Join(tmp, "hello.link")); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := TestFS(tmpfs, "hello", "hello.link"); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestDash(t *testing.T) {
+ m := MapFS{
+ "a-b/a": {Data: []byte("a-b/a")},
+ }
+ if err := TestFS(m, "a-b/a"); err != nil {
+ t.Error(err)
+ }
+}
+
+type shuffledFS MapFS
+
+func (fsys shuffledFS) Open(name string) (fs.File, error) {
+ f, err := MapFS(fsys).Open(name)
+ if err != nil {
+ return nil, err
+ }
+ return &shuffledFile{File: f}, nil
+}
+
+type shuffledFile struct{ fs.File }
+
+func (f *shuffledFile) ReadDir(n int) ([]fs.DirEntry, error) {
+ dirents, err := f.File.(fs.ReadDirFile).ReadDir(n)
+ // Shuffle in a deterministic way, all we care about is making sure that the
+ // list of directory entries is not is the lexicographic order.
+ //
+ // We do this to make sure that the TestFS test suite is not affected by the
+ // order of directory entries.
+ sort.Slice(dirents, func(i, j int) bool {
+ return dirents[i].Name() > dirents[j].Name()
+ })
+ return dirents, err
+}
+
+func TestShuffledFS(t *testing.T) {
+ fsys := shuffledFS{
+ "tmp/one": {Data: []byte("1")},
+ "tmp/two": {Data: []byte("2")},
+ "tmp/three": {Data: []byte("3")},
+ }
+ if err := TestFS(fsys, "tmp/one", "tmp/two", "tmp/three"); err != nil {
+ t.Error(err)
+ }
+}
diff --git a/src/testing/fuzz.go b/src/testing/fuzz.go
new file mode 100644
index 0000000..d50ea79
--- /dev/null
+++ b/src/testing/fuzz.go
@@ -0,0 +1,731 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testing
+
+import (
+ "errors"
+ "flag"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "strings"
+ "time"
+)
+
+func initFuzzFlags() {
+ matchFuzz = flag.String("test.fuzz", "", "run the fuzz test matching `regexp`")
+ flag.Var(&fuzzDuration, "test.fuzztime", "time to spend fuzzing; default is to run indefinitely")
+ flag.Var(&minimizeDuration, "test.fuzzminimizetime", "time to spend minimizing a value after finding a failing input")
+
+ fuzzCacheDir = flag.String("test.fuzzcachedir", "", "directory where interesting fuzzing inputs are stored (for use only by cmd/go)")
+ isFuzzWorker = flag.Bool("test.fuzzworker", false, "coordinate with the parent process to fuzz random values (for use only by cmd/go)")
+}
+
+var (
+ matchFuzz *string
+ fuzzDuration durationOrCountFlag
+ minimizeDuration = durationOrCountFlag{d: 60 * time.Second, allowZero: true}
+ fuzzCacheDir *string
+ isFuzzWorker *bool
+
+ // corpusDir is the parent directory of the fuzz test's seed corpus within
+ // the package.
+ corpusDir = "testdata/fuzz"
+)
+
+// fuzzWorkerExitCode is used as an exit code by fuzz worker processes after an
+// internal error. This distinguishes internal errors from uncontrolled panics
+// and other failures. Keep in sync with internal/fuzz.workerExitCode.
+const fuzzWorkerExitCode = 70
+
+// InternalFuzzTarget is an internal type but exported because it is
+// cross-package; it is part of the implementation of the "go test" command.
+type InternalFuzzTarget struct {
+ Name string
+ Fn func(f *F)
+}
+
+// F is a type passed to fuzz tests.
+//
+// Fuzz tests run generated inputs against a provided fuzz target, which can
+// find and report potential bugs in the code being tested.
+//
+// A fuzz test runs the seed corpus by default, which includes entries provided
+// by (*F).Add and entries in the testdata/fuzz/<FuzzTestName> directory. After
+// any necessary setup and calls to (*F).Add, the fuzz test must then call
+// (*F).Fuzz to provide the fuzz target. See the testing package documentation
+// for an example, and see the [F.Fuzz] and [F.Add] method documentation for
+// details.
+//
+// *F methods can only be called before (*F).Fuzz. Once the test is
+// executing the fuzz target, only (*T) methods can be used. The only *F methods
+// that are allowed in the (*F).Fuzz function are (*F).Failed and (*F).Name.
+type F struct {
+ common
+ fuzzContext *fuzzContext
+ testContext *testContext
+
+ // inFuzzFn is true when the fuzz function is running. Most F methods cannot
+ // be called when inFuzzFn is true.
+ inFuzzFn bool
+
+ // corpus is a set of seed corpus entries, added with F.Add and loaded
+ // from testdata.
+ corpus []corpusEntry
+
+ result fuzzResult
+ fuzzCalled bool
+}
+
+var _ TB = (*F)(nil)
+
+// corpusEntry is an alias to the same type as internal/fuzz.CorpusEntry.
+// We use a type alias because we don't want to export this type, and we can't
+// import internal/fuzz from testing.
+type corpusEntry = struct {
+ Parent string
+ Path string
+ Data []byte
+ Values []any
+ Generation int
+ IsSeed bool
+}
+
+// Helper marks the calling function as a test helper function.
+// When printing file and line information, that function will be skipped.
+// Helper may be called simultaneously from multiple goroutines.
+func (f *F) Helper() {
+ if f.inFuzzFn {
+ panic("testing: f.Helper was called inside the fuzz target, use t.Helper instead")
+ }
+
+ // common.Helper is inlined here.
+ // If we called it, it would mark F.Helper as the helper
+ // instead of the caller.
+ f.mu.Lock()
+ defer f.mu.Unlock()
+ if f.helperPCs == nil {
+ f.helperPCs = make(map[uintptr]struct{})
+ }
+ // repeating code from callerName here to save walking a stack frame
+ var pc [1]uintptr
+ n := runtime.Callers(2, pc[:]) // skip runtime.Callers + Helper
+ if n == 0 {
+ panic("testing: zero callers found")
+ }
+ if _, found := f.helperPCs[pc[0]]; !found {
+ f.helperPCs[pc[0]] = struct{}{}
+ f.helperNames = nil // map will be recreated next time it is needed
+ }
+}
+
+// Fail marks the function as having failed but continues execution.
+func (f *F) Fail() {
+ // (*F).Fail may be called by (*T).Fail, which we should allow. However, we
+ // shouldn't allow direct (*F).Fail calls from inside the (*F).Fuzz function.
+ if f.inFuzzFn {
+ panic("testing: f.Fail was called inside the fuzz target, use t.Fail instead")
+ }
+ f.common.Helper()
+ f.common.Fail()
+}
+
+// Skipped reports whether the test was skipped.
+func (f *F) Skipped() bool {
+ // (*F).Skipped may be called by tRunner, which we should allow. However, we
+ // shouldn't allow direct (*F).Skipped calls from inside the (*F).Fuzz function.
+ if f.inFuzzFn {
+ panic("testing: f.Skipped was called inside the fuzz target, use t.Skipped instead")
+ }
+ f.common.Helper()
+ return f.common.Skipped()
+}
+
+// Add will add the arguments to the seed corpus for the fuzz test. This will be
+// a no-op if called after or within the fuzz target, and args must match the
+// arguments for the fuzz target.
+func (f *F) Add(args ...any) {
+ var values []any
+ for i := range args {
+ if t := reflect.TypeOf(args[i]); !supportedTypes[t] {
+ panic(fmt.Sprintf("testing: unsupported type to Add %v", t))
+ }
+ values = append(values, args[i])
+ }
+ f.corpus = append(f.corpus, corpusEntry{Values: values, IsSeed: true, Path: fmt.Sprintf("seed#%d", len(f.corpus))})
+}
+
+// supportedTypes represents all of the supported types which can be fuzzed.
+var supportedTypes = map[reflect.Type]bool{
+ reflect.TypeOf(([]byte)("")): true,
+ reflect.TypeOf((string)("")): true,
+ reflect.TypeOf((bool)(false)): true,
+ reflect.TypeOf((byte)(0)): true,
+ reflect.TypeOf((rune)(0)): true,
+ reflect.TypeOf((float32)(0)): true,
+ reflect.TypeOf((float64)(0)): true,
+ reflect.TypeOf((int)(0)): true,
+ reflect.TypeOf((int8)(0)): true,
+ reflect.TypeOf((int16)(0)): true,
+ reflect.TypeOf((int32)(0)): true,
+ reflect.TypeOf((int64)(0)): true,
+ reflect.TypeOf((uint)(0)): true,
+ reflect.TypeOf((uint8)(0)): true,
+ reflect.TypeOf((uint16)(0)): true,
+ reflect.TypeOf((uint32)(0)): true,
+ reflect.TypeOf((uint64)(0)): true,
+}
+
+// Fuzz runs the fuzz function, ff, for fuzz testing. If ff fails for a set of
+// arguments, those arguments will be added to the seed corpus.
+//
+// ff must be a function with no return value whose first argument is *T and
+// whose remaining arguments are the types to be fuzzed.
+// For example:
+//
+// f.Fuzz(func(t *testing.T, b []byte, i int) { ... })
+//
+// The following types are allowed: []byte, string, bool, byte, rune, float32,
+// float64, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64.
+// More types may be supported in the future.
+//
+// ff must not call any *F methods, e.g. (*F).Log, (*F).Error, (*F).Skip. Use
+// the corresponding *T method instead. The only *F methods that are allowed in
+// the (*F).Fuzz function are (*F).Failed and (*F).Name.
+//
+// This function should be fast and deterministic, and its behavior should not
+// depend on shared state. No mutatable input arguments, or pointers to them,
+// should be retained between executions of the fuzz function, as the memory
+// backing them may be mutated during a subsequent invocation. ff must not
+// modify the underlying data of the arguments provided by the fuzzing engine.
+//
+// When fuzzing, F.Fuzz does not return until a problem is found, time runs out
+// (set with -fuzztime), or the test process is interrupted by a signal. F.Fuzz
+// should be called exactly once, unless F.Skip or [F.Fail] is called beforehand.
+func (f *F) Fuzz(ff any) {
+ if f.fuzzCalled {
+ panic("testing: F.Fuzz called more than once")
+ }
+ f.fuzzCalled = true
+ if f.failed {
+ return
+ }
+ f.Helper()
+
+ // ff should be in the form func(*testing.T, ...interface{})
+ fn := reflect.ValueOf(ff)
+ fnType := fn.Type()
+ if fnType.Kind() != reflect.Func {
+ panic("testing: F.Fuzz must receive a function")
+ }
+ if fnType.NumIn() < 2 || fnType.In(0) != reflect.TypeOf((*T)(nil)) {
+ panic("testing: fuzz target must receive at least two arguments, where the first argument is a *T")
+ }
+ if fnType.NumOut() != 0 {
+ panic("testing: fuzz target must not return a value")
+ }
+
+ // Save the types of the function to compare against the corpus.
+ var types []reflect.Type
+ for i := 1; i < fnType.NumIn(); i++ {
+ t := fnType.In(i)
+ if !supportedTypes[t] {
+ panic(fmt.Sprintf("testing: unsupported type for fuzzing %v", t))
+ }
+ types = append(types, t)
+ }
+
+ // Load the testdata seed corpus. Check types of entries in the testdata
+ // corpus and entries declared with F.Add.
+ //
+ // Don't load the seed corpus if this is a worker process; we won't use it.
+ if f.fuzzContext.mode != fuzzWorker {
+ for _, c := range f.corpus {
+ if err := f.fuzzContext.deps.CheckCorpus(c.Values, types); err != nil {
+ // TODO(#48302): Report the source location of the F.Add call.
+ f.Fatal(err)
+ }
+ }
+
+ // Load seed corpus
+ c, err := f.fuzzContext.deps.ReadCorpus(filepath.Join(corpusDir, f.name), types)
+ if err != nil {
+ f.Fatal(err)
+ }
+ for i := range c {
+ c[i].IsSeed = true // these are all seed corpus values
+ if f.fuzzContext.mode == fuzzCoordinator {
+ // If this is the coordinator process, zero the values, since we don't need
+ // to hold onto them.
+ c[i].Values = nil
+ }
+ }
+
+ f.corpus = append(f.corpus, c...)
+ }
+
+ // run calls fn on a given input, as a subtest with its own T.
+ // run is analogous to T.Run. The test filtering and cleanup works similarly.
+ // fn is called in its own goroutine.
+ run := func(captureOut io.Writer, e corpusEntry) (ok bool) {
+ if e.Values == nil {
+ // The corpusEntry must have non-nil Values in order to run the
+ // test. If Values is nil, it is a bug in our code.
+ panic(fmt.Sprintf("corpus file %q was not unmarshaled", e.Path))
+ }
+ if shouldFailFast() {
+ return true
+ }
+ testName := f.name
+ if e.Path != "" {
+ testName = fmt.Sprintf("%s/%s", testName, filepath.Base(e.Path))
+ }
+ if f.testContext.isFuzzing {
+ // Don't preserve subtest names while fuzzing. If fn calls T.Run,
+ // there will be a very large number of subtests with duplicate names,
+ // which will use a large amount of memory. The subtest names aren't
+ // useful since there's no way to re-run them deterministically.
+ f.testContext.match.clearSubNames()
+ }
+
+ // Record the stack trace at the point of this call so that if the subtest
+ // function - which runs in a separate stack - is marked as a helper, we can
+ // continue walking the stack into the parent test.
+ var pc [maxStackLen]uintptr
+ n := runtime.Callers(2, pc[:])
+ t := &T{
+ common: common{
+ barrier: make(chan bool),
+ signal: make(chan bool),
+ name: testName,
+ parent: &f.common,
+ level: f.level + 1,
+ creator: pc[:n],
+ chatty: f.chatty,
+ },
+ context: f.testContext,
+ }
+ if captureOut != nil {
+ // t.parent aliases f.common.
+ t.parent.w = captureOut
+ }
+ t.w = indenter{&t.common}
+ if t.chatty != nil {
+ t.chatty.Updatef(t.name, "=== RUN %s\n", t.name)
+ }
+ f.common.inFuzzFn, f.inFuzzFn = true, true
+ go tRunner(t, func(t *T) {
+ args := []reflect.Value{reflect.ValueOf(t)}
+ for _, v := range e.Values {
+ args = append(args, reflect.ValueOf(v))
+ }
+ // Before resetting the current coverage, defer the snapshot so that
+ // we make sure it is called right before the tRunner function
+ // exits, regardless of whether it was executed cleanly, panicked,
+ // or if the fuzzFn called t.Fatal.
+ if f.testContext.isFuzzing {
+ defer f.fuzzContext.deps.SnapshotCoverage()
+ f.fuzzContext.deps.ResetCoverage()
+ }
+ fn.Call(args)
+ })
+ <-t.signal
+ if t.chatty != nil && t.chatty.json {
+ t.chatty.Updatef(t.parent.name, "=== NAME %s\n", t.parent.name)
+ }
+ f.common.inFuzzFn, f.inFuzzFn = false, false
+ return !t.Failed()
+ }
+
+ switch f.fuzzContext.mode {
+ case fuzzCoordinator:
+ // Fuzzing is enabled, and this is the test process started by 'go test'.
+ // Act as the coordinator process, and coordinate workers to perform the
+ // actual fuzzing.
+ corpusTargetDir := filepath.Join(corpusDir, f.name)
+ cacheTargetDir := filepath.Join(*fuzzCacheDir, f.name)
+ err := f.fuzzContext.deps.CoordinateFuzzing(
+ fuzzDuration.d,
+ int64(fuzzDuration.n),
+ minimizeDuration.d,
+ int64(minimizeDuration.n),
+ *parallel,
+ f.corpus,
+ types,
+ corpusTargetDir,
+ cacheTargetDir)
+ if err != nil {
+ f.result = fuzzResult{Error: err}
+ f.Fail()
+ fmt.Fprintf(f.w, "%v\n", err)
+ if crashErr, ok := err.(fuzzCrashError); ok {
+ crashPath := crashErr.CrashPath()
+ fmt.Fprintf(f.w, "Failing input written to %s\n", crashPath)
+ testName := filepath.Base(crashPath)
+ fmt.Fprintf(f.w, "To re-run:\ngo test -run=%s/%s\n", f.name, testName)
+ }
+ }
+ // TODO(jayconrod,katiehockman): Aggregate statistics across workers
+ // and add to FuzzResult (ie. time taken, num iterations)
+
+ case fuzzWorker:
+ // Fuzzing is enabled, and this is a worker process. Follow instructions
+ // from the coordinator.
+ if err := f.fuzzContext.deps.RunFuzzWorker(func(e corpusEntry) error {
+ // Don't write to f.w (which points to Stdout) if running from a
+ // fuzz worker. This would become very verbose, particularly during
+ // minimization. Return the error instead, and let the caller deal
+ // with the output.
+ var buf strings.Builder
+ if ok := run(&buf, e); !ok {
+ return errors.New(buf.String())
+ }
+ return nil
+ }); err != nil {
+ // Internal errors are marked with f.Fail; user code may call this too, before F.Fuzz.
+ // The worker will exit with fuzzWorkerExitCode, indicating this is a failure
+ // (and 'go test' should exit non-zero) but a failing input should not be recorded.
+ f.Errorf("communicating with fuzzing coordinator: %v", err)
+ }
+
+ default:
+ // Fuzzing is not enabled, or will be done later. Only run the seed
+ // corpus now.
+ for _, e := range f.corpus {
+ name := fmt.Sprintf("%s/%s", f.name, filepath.Base(e.Path))
+ if _, ok, _ := f.testContext.match.fullName(nil, name); ok {
+ run(f.w, e)
+ }
+ }
+ }
+}
+
+func (f *F) report() {
+ if *isFuzzWorker || f.parent == nil {
+ return
+ }
+ dstr := fmtDuration(f.duration)
+ format := "--- %s: %s (%s)\n"
+ if f.Failed() {
+ f.flushToParent(f.name, format, "FAIL", f.name, dstr)
+ } else if f.chatty != nil {
+ if f.Skipped() {
+ f.flushToParent(f.name, format, "SKIP", f.name, dstr)
+ } else {
+ f.flushToParent(f.name, format, "PASS", f.name, dstr)
+ }
+ }
+}
+
+// fuzzResult contains the results of a fuzz run.
+type fuzzResult struct {
+ N int // The number of iterations.
+ T time.Duration // The total time taken.
+ Error error // Error is the error from the failing input
+}
+
+func (r fuzzResult) String() string {
+ if r.Error == nil {
+ return ""
+ }
+ return r.Error.Error()
+}
+
+// fuzzCrashError is satisfied by a failing input detected while fuzzing.
+// These errors are written to the seed corpus and can be re-run with 'go test'.
+// Errors within the fuzzing framework (like I/O errors between coordinator
+// and worker processes) don't satisfy this interface.
+type fuzzCrashError interface {
+ error
+ Unwrap() error
+
+ // CrashPath returns the path of the subtest that corresponds to the saved
+ // crash input file in the seed corpus. The test can be re-run with go test
+ // -run=$test/$name $test is the fuzz test name, and $name is the
+ // filepath.Base of the string returned here.
+ CrashPath() string
+}
+
+// fuzzContext holds fields common to all fuzz tests.
+type fuzzContext struct {
+ deps testDeps
+ mode fuzzMode
+}
+
+type fuzzMode uint8
+
+const (
+ seedCorpusOnly fuzzMode = iota
+ fuzzCoordinator
+ fuzzWorker
+)
+
+// runFuzzTests runs the fuzz tests matching the pattern for -run. This will
+// only run the (*F).Fuzz function for each seed corpus without using the
+// fuzzing engine to generate or mutate inputs.
+func runFuzzTests(deps testDeps, fuzzTests []InternalFuzzTarget, deadline time.Time) (ran, ok bool) {
+ ok = true
+ if len(fuzzTests) == 0 || *isFuzzWorker {
+ return ran, ok
+ }
+ m := newMatcher(deps.MatchString, *match, "-test.run", *skip)
+ var mFuzz *matcher
+ if *matchFuzz != "" {
+ mFuzz = newMatcher(deps.MatchString, *matchFuzz, "-test.fuzz", *skip)
+ }
+
+ for _, procs := range cpuList {
+ runtime.GOMAXPROCS(procs)
+ for i := uint(0); i < *count; i++ {
+ if shouldFailFast() {
+ break
+ }
+
+ tctx := newTestContext(*parallel, m)
+ tctx.deadline = deadline
+ fctx := &fuzzContext{deps: deps, mode: seedCorpusOnly}
+ root := common{w: os.Stdout} // gather output in one place
+ if Verbose() {
+ root.chatty = newChattyPrinter(root.w)
+ }
+ for _, ft := range fuzzTests {
+ if shouldFailFast() {
+ break
+ }
+ testName, matched, _ := tctx.match.fullName(nil, ft.Name)
+ if !matched {
+ continue
+ }
+ if mFuzz != nil {
+ if _, fuzzMatched, _ := mFuzz.fullName(nil, ft.Name); fuzzMatched {
+ // If this will be fuzzed, then don't run the seed corpus
+ // right now. That will happen later.
+ continue
+ }
+ }
+ f := &F{
+ common: common{
+ signal: make(chan bool),
+ barrier: make(chan bool),
+ name: testName,
+ parent: &root,
+ level: root.level + 1,
+ chatty: root.chatty,
+ },
+ testContext: tctx,
+ fuzzContext: fctx,
+ }
+ f.w = indenter{&f.common}
+ if f.chatty != nil {
+ f.chatty.Updatef(f.name, "=== RUN %s\n", f.name)
+ }
+ go fRunner(f, ft.Fn)
+ <-f.signal
+ if f.chatty != nil && f.chatty.json {
+ f.chatty.Updatef(f.parent.name, "=== NAME %s\n", f.parent.name)
+ }
+ ok = ok && !f.Failed()
+ ran = ran || f.ran
+ }
+ if !ran {
+ // There were no tests to run on this iteration.
+ // This won't change, so no reason to keep trying.
+ break
+ }
+ }
+ }
+
+ return ran, ok
+}
+
+// runFuzzing runs the fuzz test matching the pattern for -fuzz. Only one such
+// fuzz test must match. This will run the fuzzing engine to generate and
+// mutate new inputs against the fuzz target.
+//
+// If fuzzing is disabled (-test.fuzz is not set), runFuzzing
+// returns immediately.
+func runFuzzing(deps testDeps, fuzzTests []InternalFuzzTarget) (ok bool) {
+ if len(fuzzTests) == 0 || *matchFuzz == "" {
+ return true
+ }
+ m := newMatcher(deps.MatchString, *matchFuzz, "-test.fuzz", *skip)
+ tctx := newTestContext(1, m)
+ tctx.isFuzzing = true
+ fctx := &fuzzContext{
+ deps: deps,
+ }
+ root := common{w: os.Stdout}
+ if *isFuzzWorker {
+ root.w = io.Discard
+ fctx.mode = fuzzWorker
+ } else {
+ fctx.mode = fuzzCoordinator
+ }
+ if Verbose() && !*isFuzzWorker {
+ root.chatty = newChattyPrinter(root.w)
+ }
+ var fuzzTest *InternalFuzzTarget
+ var testName string
+ var matched []string
+ for i := range fuzzTests {
+ name, ok, _ := tctx.match.fullName(nil, fuzzTests[i].Name)
+ if !ok {
+ continue
+ }
+ matched = append(matched, name)
+ fuzzTest = &fuzzTests[i]
+ testName = name
+ }
+ if len(matched) == 0 {
+ fmt.Fprintln(os.Stderr, "testing: warning: no fuzz tests to fuzz")
+ return true
+ }
+ if len(matched) > 1 {
+ fmt.Fprintf(os.Stderr, "testing: will not fuzz, -fuzz matches more than one fuzz test: %v\n", matched)
+ return false
+ }
+
+ f := &F{
+ common: common{
+ signal: make(chan bool),
+ barrier: nil, // T.Parallel has no effect when fuzzing.
+ name: testName,
+ parent: &root,
+ level: root.level + 1,
+ chatty: root.chatty,
+ },
+ fuzzContext: fctx,
+ testContext: tctx,
+ }
+ f.w = indenter{&f.common}
+ if f.chatty != nil {
+ f.chatty.Updatef(f.name, "=== RUN %s\n", f.name)
+ }
+ go fRunner(f, fuzzTest.Fn)
+ <-f.signal
+ if f.chatty != nil {
+ f.chatty.Updatef(f.parent.name, "=== NAME %s\n", f.parent.name)
+ }
+ return !f.failed
+}
+
+// fRunner wraps a call to a fuzz test and ensures that cleanup functions are
+// called and status flags are set. fRunner should be called in its own
+// goroutine. To wait for its completion, receive from f.signal.
+//
+// fRunner is analogous to tRunner, which wraps subtests started with T.Run.
+// Unit tests and fuzz tests work a little differently, so for now, these
+// functions aren't consolidated. In particular, because there are no F.Run and
+// F.Parallel methods, i.e., no fuzz sub-tests or parallel fuzz tests, a few
+// simplifications are made. We also require that F.Fuzz, F.Skip, or F.Fail is
+// called.
+func fRunner(f *F, fn func(*F)) {
+ // When this goroutine is done, either because runtime.Goexit was called, a
+ // panic started, or fn returned normally, record the duration and send
+ // t.signal, indicating the fuzz test is done.
+ defer func() {
+ // Detect whether the fuzz test panicked or called runtime.Goexit
+ // without calling F.Fuzz, F.Fail, or F.Skip. If it did, panic (possibly
+ // replacing a nil panic value). Nothing should recover after fRunner
+ // unwinds, so this should crash the process and print stack.
+ // Unfortunately, recovering here adds stack frames, but the location of
+ // the original panic should still be
+ // clear.
+ f.checkRaces()
+ if f.Failed() {
+ numFailed.Add(1)
+ }
+ err := recover()
+ if err == nil {
+ f.mu.RLock()
+ fuzzNotCalled := !f.fuzzCalled && !f.skipped && !f.failed
+ if !f.finished && !f.skipped && !f.failed {
+ err = errNilPanicOrGoexit
+ }
+ f.mu.RUnlock()
+ if fuzzNotCalled && err == nil {
+ f.Error("returned without calling F.Fuzz, F.Fail, or F.Skip")
+ }
+ }
+
+ // Use a deferred call to ensure that we report that the test is
+ // complete even if a cleanup function calls F.FailNow. See issue 41355.
+ didPanic := false
+ defer func() {
+ if !didPanic {
+ // Only report that the test is complete if it doesn't panic,
+ // as otherwise the test binary can exit before the panic is
+ // reported to the user. See issue 41479.
+ f.signal <- true
+ }
+ }()
+
+ // If we recovered a panic or inappropriate runtime.Goexit, fail the test,
+ // flush the output log up to the root, then panic.
+ doPanic := func(err any) {
+ f.Fail()
+ if r := f.runCleanup(recoverAndReturnPanic); r != nil {
+ f.Logf("cleanup panicked with %v", r)
+ }
+ for root := &f.common; root.parent != nil; root = root.parent {
+ root.mu.Lock()
+ root.duration += time.Since(root.start)
+ d := root.duration
+ root.mu.Unlock()
+ root.flushToParent(root.name, "--- FAIL: %s (%s)\n", root.name, fmtDuration(d))
+ }
+ didPanic = true
+ panic(err)
+ }
+ if err != nil {
+ doPanic(err)
+ }
+
+ // No panic or inappropriate Goexit.
+ f.duration += time.Since(f.start)
+
+ if len(f.sub) > 0 {
+ // Unblock inputs that called T.Parallel while running the seed corpus.
+ // This only affects fuzz tests run as normal tests.
+ // While fuzzing, T.Parallel has no effect, so f.sub is empty, and this
+ // branch is not taken. f.barrier is nil in that case.
+ f.testContext.release()
+ close(f.barrier)
+ // Wait for the subtests to complete.
+ for _, sub := range f.sub {
+ <-sub.signal
+ }
+ cleanupStart := time.Now()
+ err := f.runCleanup(recoverAndReturnPanic)
+ f.duration += time.Since(cleanupStart)
+ if err != nil {
+ doPanic(err)
+ }
+ }
+
+ // Report after all subtests have finished.
+ f.report()
+ f.done = true
+ f.setRan()
+ }()
+ defer func() {
+ if len(f.sub) == 0 {
+ f.runCleanup(normalPanic)
+ }
+ }()
+
+ f.start = time.Now()
+ f.resetRaces()
+ fn(f)
+
+ // Code beyond this point will not be executed when FailNow or SkipNow
+ // is invoked.
+ f.mu.Lock()
+ f.finished = true
+ f.mu.Unlock()
+}
diff --git a/src/testing/helper_test.go b/src/testing/helper_test.go
new file mode 100644
index 0000000..da5622f
--- /dev/null
+++ b/src/testing/helper_test.go
@@ -0,0 +1,116 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testing_test
+
+import (
+ "internal/testenv"
+ "os"
+ "regexp"
+ "strings"
+ "testing"
+)
+
+func TestTBHelper(t *testing.T) {
+ if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
+ testTestHelper(t)
+
+ // Check that calling Helper from inside a top-level test function
+ // has no effect.
+ t.Helper()
+ t.Error("8")
+ return
+ }
+
+ testenv.MustHaveExec(t)
+ t.Parallel()
+
+ exe, err := os.Executable()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ cmd := testenv.Command(t, exe, "-test.run=^TestTBHelper$")
+ cmd = testenv.CleanCmdEnv(cmd)
+ cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
+ out, _ := cmd.CombinedOutput()
+
+ want := `--- FAIL: TestTBHelper \([^)]+\)
+ helperfuncs_test.go:15: 0
+ helperfuncs_test.go:47: 1
+ helperfuncs_test.go:24: 2
+ helperfuncs_test.go:49: 3
+ helperfuncs_test.go:56: 4
+ --- FAIL: TestTBHelper/sub \([^)]+\)
+ helperfuncs_test.go:59: 5
+ helperfuncs_test.go:24: 6
+ helperfuncs_test.go:58: 7
+ --- FAIL: TestTBHelper/sub2 \([^)]+\)
+ helperfuncs_test.go:80: 11
+ helperfuncs_test.go:84: recover 12
+ helperfuncs_test.go:86: GenericFloat64
+ helperfuncs_test.go:87: GenericInt
+ helper_test.go:22: 8
+ helperfuncs_test.go:73: 9
+ helperfuncs_test.go:69: 10
+`
+ if !regexp.MustCompile(want).Match(out) {
+ t.Errorf("got output:\n\n%s\nwant matching:\n\n%s", out, want)
+ }
+}
+
+func TestTBHelperParallel(t *testing.T) {
+ if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
+ parallelTestHelper(t)
+ return
+ }
+
+ testenv.MustHaveExec(t)
+ t.Parallel()
+
+ exe, err := os.Executable()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ cmd := testenv.Command(t, exe, "-test.run=^TestTBHelperParallel$")
+ cmd = testenv.CleanCmdEnv(cmd)
+ cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
+ out, _ := cmd.CombinedOutput()
+
+ t.Logf("output:\n%s", out)
+
+ lines := strings.Split(strings.TrimSpace(string(out)), "\n")
+
+ // We expect to see one "--- FAIL" line at the start
+ // of the log, five lines of "parallel" logging,
+ // and a final "FAIL" line at the end of the test.
+ const wantLines = 7
+
+ if len(lines) != wantLines {
+ t.Fatalf("parallelTestHelper gave %d lines of output; want %d", len(lines), wantLines)
+ }
+ want := "helperfuncs_test.go:24: parallel"
+ if got := strings.TrimSpace(lines[1]); got != want {
+ t.Errorf("got second output line %q; want %q", got, want)
+ }
+}
+
+func BenchmarkTBHelper(b *testing.B) {
+ f1 := func() {
+ b.Helper()
+ }
+ f2 := func() {
+ b.Helper()
+ }
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ if i&1 == 0 {
+ f1()
+ } else {
+ f2()
+ }
+ }
+}
diff --git a/src/testing/helperfuncs_test.go b/src/testing/helperfuncs_test.go
new file mode 100644
index 0000000..f0295f3
--- /dev/null
+++ b/src/testing/helperfuncs_test.go
@@ -0,0 +1,124 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testing_test
+
+import (
+ "sync"
+ "testing"
+)
+
+// The line numbering of this file is important for TestTBHelper.
+
+func notHelper(t *testing.T, msg string) {
+ t.Error(msg)
+}
+
+func helper(t *testing.T, msg string) {
+ t.Helper()
+ t.Error(msg)
+}
+
+func notHelperCallingHelper(t *testing.T, msg string) {
+ helper(t, msg)
+}
+
+func helperCallingHelper(t *testing.T, msg string) {
+ t.Helper()
+ helper(t, msg)
+}
+
+func genericHelper[G any](t *testing.T, msg string) {
+ t.Helper()
+ t.Error(msg)
+}
+
+var genericIntHelper = genericHelper[int]
+
+func testTestHelper(t *testing.T) {
+ testHelper(t)
+}
+
+func testHelper(t *testing.T) {
+ // Check combinations of directly and indirectly
+ // calling helper functions.
+ notHelper(t, "0")
+ helper(t, "1")
+ notHelperCallingHelper(t, "2")
+ helperCallingHelper(t, "3")
+
+ // Check a function literal closing over t that uses Helper.
+ fn := func(msg string) {
+ t.Helper()
+ t.Error(msg)
+ }
+ fn("4")
+
+ t.Run("sub", func(t *testing.T) {
+ helper(t, "5")
+ notHelperCallingHelper(t, "6")
+ // Check that calling Helper from inside a subtest entry function
+ // works as if it were in an ordinary function call.
+ t.Helper()
+ t.Error("7")
+ })
+
+ // Check that right caller is reported for func passed to Cleanup when
+ // multiple cleanup functions have been registered.
+ t.Cleanup(func() {
+ t.Helper()
+ t.Error("10")
+ })
+ t.Cleanup(func() {
+ t.Helper()
+ t.Error("9")
+ })
+
+ // Check that helper-ness propagates up through subtests
+ // to helpers above. See https://golang.org/issue/44887.
+ helperSubCallingHelper(t, "11")
+
+ // Check that helper-ness propagates up through panic/recover.
+ // See https://golang.org/issue/31154.
+ recoverHelper(t, "12")
+
+ genericHelper[float64](t, "GenericFloat64")
+ genericIntHelper(t, "GenericInt")
+}
+
+func parallelTestHelper(t *testing.T) {
+ var wg sync.WaitGroup
+ for i := 0; i < 5; i++ {
+ wg.Add(1)
+ go func() {
+ notHelperCallingHelper(t, "parallel")
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+}
+
+func helperSubCallingHelper(t *testing.T, msg string) {
+ t.Helper()
+ t.Run("sub2", func(t *testing.T) {
+ t.Helper()
+ t.Fatal(msg)
+ })
+}
+
+func recoverHelper(t *testing.T, msg string) {
+ t.Helper()
+ defer func() {
+ t.Helper()
+ if err := recover(); err != nil {
+ t.Errorf("recover %s", err)
+ }
+ }()
+ doPanic(t, msg)
+}
+
+func doPanic(t *testing.T, msg string) {
+ t.Helper()
+ panic(msg)
+}
diff --git a/src/testing/internal/testdeps/deps.go b/src/testing/internal/testdeps/deps.go
new file mode 100644
index 0000000..8683075
--- /dev/null
+++ b/src/testing/internal/testdeps/deps.go
@@ -0,0 +1,199 @@
+// Copyright 2016 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 testdeps provides access to dependencies needed by test execution.
+//
+// This package is imported by the generated main package, which passes
+// TestDeps into testing.Main. This allows tests to use packages at run time
+// without making those packages direct dependencies of package testing.
+// Direct dependencies of package testing are harder to write tests for.
+package testdeps
+
+import (
+ "bufio"
+ "context"
+ "internal/fuzz"
+ "internal/testlog"
+ "io"
+ "os"
+ "os/signal"
+ "reflect"
+ "regexp"
+ "runtime/pprof"
+ "strings"
+ "sync"
+ "time"
+)
+
+// TestDeps is an implementation of the testing.testDeps interface,
+// suitable for passing to [testing.MainStart].
+type TestDeps struct{}
+
+var matchPat string
+var matchRe *regexp.Regexp
+
+func (TestDeps) MatchString(pat, str string) (result bool, err error) {
+ if matchRe == nil || matchPat != pat {
+ matchPat = pat
+ matchRe, err = regexp.Compile(matchPat)
+ if err != nil {
+ return
+ }
+ }
+ return matchRe.MatchString(str), nil
+}
+
+func (TestDeps) StartCPUProfile(w io.Writer) error {
+ return pprof.StartCPUProfile(w)
+}
+
+func (TestDeps) StopCPUProfile() {
+ pprof.StopCPUProfile()
+}
+
+func (TestDeps) WriteProfileTo(name string, w io.Writer, debug int) error {
+ return pprof.Lookup(name).WriteTo(w, debug)
+}
+
+// ImportPath is the import path of the testing binary, set by the generated main function.
+var ImportPath string
+
+func (TestDeps) ImportPath() string {
+ return ImportPath
+}
+
+// testLog implements testlog.Interface, logging actions by package os.
+type testLog struct {
+ mu sync.Mutex
+ w *bufio.Writer
+ set bool
+}
+
+func (l *testLog) Getenv(key string) {
+ l.add("getenv", key)
+}
+
+func (l *testLog) Open(name string) {
+ l.add("open", name)
+}
+
+func (l *testLog) Stat(name string) {
+ l.add("stat", name)
+}
+
+func (l *testLog) Chdir(name string) {
+ l.add("chdir", name)
+}
+
+// add adds the (op, name) pair to the test log.
+func (l *testLog) add(op, name string) {
+ if strings.Contains(name, "\n") || name == "" {
+ return
+ }
+
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ if l.w == nil {
+ return
+ }
+ l.w.WriteString(op)
+ l.w.WriteByte(' ')
+ l.w.WriteString(name)
+ l.w.WriteByte('\n')
+}
+
+var log testLog
+
+func (TestDeps) StartTestLog(w io.Writer) {
+ log.mu.Lock()
+ log.w = bufio.NewWriter(w)
+ if !log.set {
+ // Tests that define TestMain and then run m.Run multiple times
+ // will call StartTestLog/StopTestLog multiple times.
+ // Checking log.set avoids calling testlog.SetLogger multiple times
+ // (which will panic) and also avoids writing the header multiple times.
+ log.set = true
+ testlog.SetLogger(&log)
+ log.w.WriteString("# test log\n") // known to cmd/go/internal/test/test.go
+ }
+ log.mu.Unlock()
+}
+
+func (TestDeps) StopTestLog() error {
+ log.mu.Lock()
+ defer log.mu.Unlock()
+ err := log.w.Flush()
+ log.w = nil
+ return err
+}
+
+// SetPanicOnExit0 tells the os package whether to panic on os.Exit(0).
+func (TestDeps) SetPanicOnExit0(v bool) {
+ testlog.SetPanicOnExit0(v)
+}
+
+func (TestDeps) CoordinateFuzzing(
+ timeout time.Duration,
+ limit int64,
+ minimizeTimeout time.Duration,
+ minimizeLimit int64,
+ parallel int,
+ seed []fuzz.CorpusEntry,
+ types []reflect.Type,
+ corpusDir,
+ cacheDir string) (err error) {
+ // Fuzzing may be interrupted with a timeout or if the user presses ^C.
+ // In either case, we'll stop worker processes gracefully and save
+ // crashers and interesting values.
+ ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
+ defer cancel()
+ err = fuzz.CoordinateFuzzing(ctx, fuzz.CoordinateFuzzingOpts{
+ Log: os.Stderr,
+ Timeout: timeout,
+ Limit: limit,
+ MinimizeTimeout: minimizeTimeout,
+ MinimizeLimit: minimizeLimit,
+ Parallel: parallel,
+ Seed: seed,
+ Types: types,
+ CorpusDir: corpusDir,
+ CacheDir: cacheDir,
+ })
+ if err == ctx.Err() {
+ return nil
+ }
+ return err
+}
+
+func (TestDeps) RunFuzzWorker(fn func(fuzz.CorpusEntry) error) error {
+ // Worker processes may or may not receive a signal when the user presses ^C
+ // On POSIX operating systems, a signal sent to a process group is delivered
+ // to all processes in that group. This is not the case on Windows.
+ // If the worker is interrupted, return quickly and without error.
+ // If only the coordinator process is interrupted, it tells each worker
+ // process to stop by closing its "fuzz_in" pipe.
+ ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
+ defer cancel()
+ err := fuzz.RunFuzzWorker(ctx, fn)
+ if err == ctx.Err() {
+ return nil
+ }
+ return err
+}
+
+func (TestDeps) ReadCorpus(dir string, types []reflect.Type) ([]fuzz.CorpusEntry, error) {
+ return fuzz.ReadCorpus(dir, types)
+}
+
+func (TestDeps) CheckCorpus(vals []any, types []reflect.Type) error {
+ return fuzz.CheckCorpus(vals, types)
+}
+
+func (TestDeps) ResetCoverage() {
+ fuzz.ResetCoverage()
+}
+
+func (TestDeps) SnapshotCoverage() {
+ fuzz.SnapshotCoverage()
+}
diff --git a/src/testing/iotest/example_test.go b/src/testing/iotest/example_test.go
new file mode 100644
index 0000000..10f6bd3
--- /dev/null
+++ b/src/testing/iotest/example_test.go
@@ -0,0 +1,22 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package iotest_test
+
+import (
+ "errors"
+ "fmt"
+ "testing/iotest"
+)
+
+func ExampleErrReader() {
+ // A reader that always returns a custom error.
+ r := iotest.ErrReader(errors.New("custom error"))
+ n, err := r.Read(nil)
+ fmt.Printf("n: %d\nerr: %q\n", n, err)
+
+ // Output:
+ // n: 0
+ // err: "custom error"
+}
diff --git a/src/testing/iotest/logger.go b/src/testing/iotest/logger.go
new file mode 100644
index 0000000..10d0cb5
--- /dev/null
+++ b/src/testing/iotest/logger.go
@@ -0,0 +1,54 @@
+// Copyright 2009 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 iotest
+
+import (
+ "io"
+ "log"
+)
+
+type writeLogger struct {
+ prefix string
+ w io.Writer
+}
+
+func (l *writeLogger) Write(p []byte) (n int, err error) {
+ n, err = l.w.Write(p)
+ if err != nil {
+ log.Printf("%s %x: %v", l.prefix, p[0:n], err)
+ } else {
+ log.Printf("%s %x", l.prefix, p[0:n])
+ }
+ return
+}
+
+// NewWriteLogger returns a writer that behaves like w except
+// that it logs (using [log.Printf]) each write to standard error,
+// printing the prefix and the hexadecimal data written.
+func NewWriteLogger(prefix string, w io.Writer) io.Writer {
+ return &writeLogger{prefix, w}
+}
+
+type readLogger struct {
+ prefix string
+ r io.Reader
+}
+
+func (l *readLogger) Read(p []byte) (n int, err error) {
+ n, err = l.r.Read(p)
+ if err != nil {
+ log.Printf("%s %x: %v", l.prefix, p[0:n], err)
+ } else {
+ log.Printf("%s %x", l.prefix, p[0:n])
+ }
+ return
+}
+
+// NewReadLogger returns a reader that behaves like r except
+// that it logs (using [log.Printf]) each read to standard error,
+// printing the prefix and the hexadecimal data read.
+func NewReadLogger(prefix string, r io.Reader) io.Reader {
+ return &readLogger{prefix, r}
+}
diff --git a/src/testing/iotest/logger_test.go b/src/testing/iotest/logger_test.go
new file mode 100644
index 0000000..7a7d0aa
--- /dev/null
+++ b/src/testing/iotest/logger_test.go
@@ -0,0 +1,153 @@
+// Copyright 2019 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 iotest
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "log"
+ "strings"
+ "testing"
+)
+
+type errWriter struct {
+ err error
+}
+
+func (w errWriter) Write([]byte) (int, error) {
+ return 0, w.err
+}
+
+func TestWriteLogger(t *testing.T) {
+ olw := log.Writer()
+ olf := log.Flags()
+ olp := log.Prefix()
+
+ // Revert the original log settings before we exit.
+ defer func() {
+ log.SetFlags(olf)
+ log.SetPrefix(olp)
+ log.SetOutput(olw)
+ }()
+
+ lOut := new(strings.Builder)
+ log.SetPrefix("lw: ")
+ log.SetOutput(lOut)
+ log.SetFlags(0)
+
+ lw := new(strings.Builder)
+ wl := NewWriteLogger("write:", lw)
+ if _, err := wl.Write([]byte("Hello, World!")); err != nil {
+ t.Fatalf("Unexpectedly failed to write: %v", err)
+ }
+
+ if g, w := lw.String(), "Hello, World!"; g != w {
+ t.Errorf("WriteLogger mismatch\n\tgot: %q\n\twant: %q", g, w)
+ }
+ wantLogWithHex := fmt.Sprintf("lw: write: %x\n", "Hello, World!")
+ if g, w := lOut.String(), wantLogWithHex; g != w {
+ t.Errorf("WriteLogger mismatch\n\tgot: %q\n\twant: %q", g, w)
+ }
+}
+
+func TestWriteLogger_errorOnWrite(t *testing.T) {
+ olw := log.Writer()
+ olf := log.Flags()
+ olp := log.Prefix()
+
+ // Revert the original log settings before we exit.
+ defer func() {
+ log.SetFlags(olf)
+ log.SetPrefix(olp)
+ log.SetOutput(olw)
+ }()
+
+ lOut := new(strings.Builder)
+ log.SetPrefix("lw: ")
+ log.SetOutput(lOut)
+ log.SetFlags(0)
+
+ lw := errWriter{err: errors.New("Write Error!")}
+ wl := NewWriteLogger("write:", lw)
+ if _, err := wl.Write([]byte("Hello, World!")); err == nil {
+ t.Fatalf("Unexpectedly succeeded to write: %v", err)
+ }
+
+ wantLogWithHex := fmt.Sprintf("lw: write: %x: %v\n", "", "Write Error!")
+ if g, w := lOut.String(), wantLogWithHex; g != w {
+ t.Errorf("WriteLogger mismatch\n\tgot: %q\n\twant: %q", g, w)
+ }
+}
+
+func TestReadLogger(t *testing.T) {
+ olw := log.Writer()
+ olf := log.Flags()
+ olp := log.Prefix()
+
+ // Revert the original log settings before we exit.
+ defer func() {
+ log.SetFlags(olf)
+ log.SetPrefix(olp)
+ log.SetOutput(olw)
+ }()
+
+ lOut := new(strings.Builder)
+ log.SetPrefix("lr: ")
+ log.SetOutput(lOut)
+ log.SetFlags(0)
+
+ data := []byte("Hello, World!")
+ p := make([]byte, len(data))
+ lr := bytes.NewReader(data)
+ rl := NewReadLogger("read:", lr)
+
+ n, err := rl.Read(p)
+ if err != nil {
+ t.Fatalf("Unexpectedly failed to read: %v", err)
+ }
+
+ if g, w := p[:n], data; !bytes.Equal(g, w) {
+ t.Errorf("ReadLogger mismatch\n\tgot: %q\n\twant: %q", g, w)
+ }
+
+ wantLogWithHex := fmt.Sprintf("lr: read: %x\n", "Hello, World!")
+ if g, w := lOut.String(), wantLogWithHex; g != w {
+ t.Errorf("ReadLogger mismatch\n\tgot: %q\n\twant: %q", g, w)
+ }
+}
+
+func TestReadLogger_errorOnRead(t *testing.T) {
+ olw := log.Writer()
+ olf := log.Flags()
+ olp := log.Prefix()
+
+ // Revert the original log settings before we exit.
+ defer func() {
+ log.SetFlags(olf)
+ log.SetPrefix(olp)
+ log.SetOutput(olw)
+ }()
+
+ lOut := new(strings.Builder)
+ log.SetPrefix("lr: ")
+ log.SetOutput(lOut)
+ log.SetFlags(0)
+
+ data := []byte("Hello, World!")
+ p := make([]byte, len(data))
+
+ lr := ErrReader(errors.New("io failure"))
+ rl := NewReadLogger("read", lr)
+ n, err := rl.Read(p)
+ if err == nil {
+ t.Fatalf("Unexpectedly succeeded to read: %v", err)
+ }
+
+ wantLogWithHex := fmt.Sprintf("lr: read %x: io failure\n", p[:n])
+ if g, w := lOut.String(), wantLogWithHex; g != w {
+ t.Errorf("ReadLogger mismatch\n\tgot: %q\n\twant: %q", g, w)
+ }
+}
diff --git a/src/testing/iotest/reader.go b/src/testing/iotest/reader.go
new file mode 100644
index 0000000..8529e1c
--- /dev/null
+++ b/src/testing/iotest/reader.go
@@ -0,0 +1,268 @@
+// Copyright 2009 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 iotest implements Readers and Writers useful mainly for testing.
+package iotest
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+)
+
+// OneByteReader returns a Reader that implements
+// each non-empty Read by reading one byte from r.
+func OneByteReader(r io.Reader) io.Reader { return &oneByteReader{r} }
+
+type oneByteReader struct {
+ r io.Reader
+}
+
+func (r *oneByteReader) Read(p []byte) (int, error) {
+ if len(p) == 0 {
+ return 0, nil
+ }
+ return r.r.Read(p[0:1])
+}
+
+// HalfReader returns a Reader that implements Read
+// by reading half as many requested bytes from r.
+func HalfReader(r io.Reader) io.Reader { return &halfReader{r} }
+
+type halfReader struct {
+ r io.Reader
+}
+
+func (r *halfReader) Read(p []byte) (int, error) {
+ return r.r.Read(p[0 : (len(p)+1)/2])
+}
+
+// DataErrReader changes the way errors are handled by a Reader. Normally, a
+// Reader returns an error (typically EOF) from the first Read call after the
+// last piece of data is read. DataErrReader wraps a Reader and changes its
+// behavior so the final error is returned along with the final data, instead
+// of in the first call after the final data.
+func DataErrReader(r io.Reader) io.Reader { return &dataErrReader{r, nil, make([]byte, 1024)} }
+
+type dataErrReader struct {
+ r io.Reader
+ unread []byte
+ data []byte
+}
+
+func (r *dataErrReader) Read(p []byte) (n int, err error) {
+ // loop because first call needs two reads:
+ // one to get data and a second to look for an error.
+ for {
+ if len(r.unread) == 0 {
+ n1, err1 := r.r.Read(r.data)
+ r.unread = r.data[0:n1]
+ err = err1
+ }
+ if n > 0 || err != nil {
+ break
+ }
+ n = copy(p, r.unread)
+ r.unread = r.unread[n:]
+ }
+ return
+}
+
+// ErrTimeout is a fake timeout error.
+var ErrTimeout = errors.New("timeout")
+
+// TimeoutReader returns [ErrTimeout] on the second read
+// with no data. Subsequent calls to read succeed.
+func TimeoutReader(r io.Reader) io.Reader { return &timeoutReader{r, 0} }
+
+type timeoutReader struct {
+ r io.Reader
+ count int
+}
+
+func (r *timeoutReader) Read(p []byte) (int, error) {
+ r.count++
+ if r.count == 2 {
+ return 0, ErrTimeout
+ }
+ return r.r.Read(p)
+}
+
+// ErrReader returns an [io.Reader] that returns 0, err from all Read calls.
+func ErrReader(err error) io.Reader {
+ return &errReader{err: err}
+}
+
+type errReader struct {
+ err error
+}
+
+func (r *errReader) Read(p []byte) (int, error) {
+ return 0, r.err
+}
+
+type smallByteReader struct {
+ r io.Reader
+ off int
+ n int
+}
+
+func (r *smallByteReader) Read(p []byte) (int, error) {
+ if len(p) == 0 {
+ return 0, nil
+ }
+ r.n = r.n%3 + 1
+ n := r.n
+ if n > len(p) {
+ n = len(p)
+ }
+ n, err := r.r.Read(p[0:n])
+ if err != nil && err != io.EOF {
+ err = fmt.Errorf("Read(%d bytes at offset %d): %v", n, r.off, err)
+ }
+ r.off += n
+ return n, err
+}
+
+// TestReader tests that reading from r returns the expected file content.
+// It does reads of different sizes, until EOF.
+// If r implements [io.ReaderAt] or [io.Seeker], TestReader also checks
+// that those operations behave as they should.
+//
+// If TestReader finds any misbehaviors, it returns an error reporting them.
+// The error text may span multiple lines.
+func TestReader(r io.Reader, content []byte) error {
+ if len(content) > 0 {
+ n, err := r.Read(nil)
+ if n != 0 || err != nil {
+ return fmt.Errorf("Read(0) = %d, %v, want 0, nil", n, err)
+ }
+ }
+
+ data, err := io.ReadAll(&smallByteReader{r: r})
+ if err != nil {
+ return err
+ }
+ if !bytes.Equal(data, content) {
+ return fmt.Errorf("ReadAll(small amounts) = %q\n\twant %q", data, content)
+ }
+ n, err := r.Read(make([]byte, 10))
+ if n != 0 || err != io.EOF {
+ return fmt.Errorf("Read(10) at EOF = %v, %v, want 0, EOF", n, err)
+ }
+
+ if r, ok := r.(io.ReadSeeker); ok {
+ // Seek(0, 1) should report the current file position (EOF).
+ if off, err := r.Seek(0, 1); off != int64(len(content)) || err != nil {
+ return fmt.Errorf("Seek(0, 1) from EOF = %d, %v, want %d, nil", off, err, len(content))
+ }
+
+ // Seek backward partway through file, in two steps.
+ // If middle == 0, len(content) == 0, can't use the -1 and +1 seeks.
+ middle := len(content) - len(content)/3
+ if middle > 0 {
+ if off, err := r.Seek(-1, 1); off != int64(len(content)-1) || err != nil {
+ return fmt.Errorf("Seek(-1, 1) from EOF = %d, %v, want %d, nil", -off, err, len(content)-1)
+ }
+ if off, err := r.Seek(int64(-len(content)/3), 1); off != int64(middle-1) || err != nil {
+ return fmt.Errorf("Seek(%d, 1) from %d = %d, %v, want %d, nil", -len(content)/3, len(content)-1, off, err, middle-1)
+ }
+ if off, err := r.Seek(+1, 1); off != int64(middle) || err != nil {
+ return fmt.Errorf("Seek(+1, 1) from %d = %d, %v, want %d, nil", middle-1, off, err, middle)
+ }
+ }
+
+ // Seek(0, 1) should report the current file position (middle).
+ if off, err := r.Seek(0, 1); off != int64(middle) || err != nil {
+ return fmt.Errorf("Seek(0, 1) from %d = %d, %v, want %d, nil", middle, off, err, middle)
+ }
+
+ // Reading forward should return the last part of the file.
+ data, err := io.ReadAll(&smallByteReader{r: r})
+ if err != nil {
+ return fmt.Errorf("ReadAll from offset %d: %v", middle, err)
+ }
+ if !bytes.Equal(data, content[middle:]) {
+ return fmt.Errorf("ReadAll from offset %d = %q\n\twant %q", middle, data, content[middle:])
+ }
+
+ // Seek relative to end of file, but start elsewhere.
+ if off, err := r.Seek(int64(middle/2), 0); off != int64(middle/2) || err != nil {
+ return fmt.Errorf("Seek(%d, 0) from EOF = %d, %v, want %d, nil", middle/2, off, err, middle/2)
+ }
+ if off, err := r.Seek(int64(-len(content)/3), 2); off != int64(middle) || err != nil {
+ return fmt.Errorf("Seek(%d, 2) from %d = %d, %v, want %d, nil", -len(content)/3, middle/2, off, err, middle)
+ }
+
+ // Reading forward should return the last part of the file (again).
+ data, err = io.ReadAll(&smallByteReader{r: r})
+ if err != nil {
+ return fmt.Errorf("ReadAll from offset %d: %v", middle, err)
+ }
+ if !bytes.Equal(data, content[middle:]) {
+ return fmt.Errorf("ReadAll from offset %d = %q\n\twant %q", middle, data, content[middle:])
+ }
+
+ // Absolute seek & read forward.
+ if off, err := r.Seek(int64(middle/2), 0); off != int64(middle/2) || err != nil {
+ return fmt.Errorf("Seek(%d, 0) from EOF = %d, %v, want %d, nil", middle/2, off, err, middle/2)
+ }
+ data, err = io.ReadAll(r)
+ if err != nil {
+ return fmt.Errorf("ReadAll from offset %d: %v", middle/2, err)
+ }
+ if !bytes.Equal(data, content[middle/2:]) {
+ return fmt.Errorf("ReadAll from offset %d = %q\n\twant %q", middle/2, data, content[middle/2:])
+ }
+ }
+
+ if r, ok := r.(io.ReaderAt); ok {
+ data := make([]byte, len(content), len(content)+1)
+ for i := range data {
+ data[i] = 0xfe
+ }
+ n, err := r.ReadAt(data, 0)
+ if n != len(data) || err != nil && err != io.EOF {
+ return fmt.Errorf("ReadAt(%d, 0) = %v, %v, want %d, nil or EOF", len(data), n, err, len(data))
+ }
+ if !bytes.Equal(data, content) {
+ return fmt.Errorf("ReadAt(%d, 0) = %q\n\twant %q", len(data), data, content)
+ }
+
+ n, err = r.ReadAt(data[:1], int64(len(data)))
+ if n != 0 || err != io.EOF {
+ return fmt.Errorf("ReadAt(1, %d) = %v, %v, want 0, EOF", len(data), n, err)
+ }
+
+ for i := range data {
+ data[i] = 0xfe
+ }
+ n, err = r.ReadAt(data[:cap(data)], 0)
+ if n != len(data) || err != io.EOF {
+ return fmt.Errorf("ReadAt(%d, 0) = %v, %v, want %d, EOF", cap(data), n, err, len(data))
+ }
+ if !bytes.Equal(data, content) {
+ return fmt.Errorf("ReadAt(%d, 0) = %q\n\twant %q", len(data), data, content)
+ }
+
+ for i := range data {
+ data[i] = 0xfe
+ }
+ for i := range data {
+ n, err = r.ReadAt(data[i:i+1], int64(i))
+ if n != 1 || err != nil && (i != len(data)-1 || err != io.EOF) {
+ want := "nil"
+ if i == len(data)-1 {
+ want = "nil or EOF"
+ }
+ return fmt.Errorf("ReadAt(1, %d) = %v, %v, want 1, %s", i, n, err, want)
+ }
+ if data[i] != content[i] {
+ return fmt.Errorf("ReadAt(1, %d) = %q want %q", i, data[i:i+1], content[i:i+1])
+ }
+ }
+ }
+ return nil
+}
diff --git a/src/testing/iotest/reader_test.go b/src/testing/iotest/reader_test.go
new file mode 100644
index 0000000..1d22237
--- /dev/null
+++ b/src/testing/iotest/reader_test.go
@@ -0,0 +1,261 @@
+// Copyright 2019 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 iotest
+
+import (
+ "bytes"
+ "errors"
+ "io"
+ "strings"
+ "testing"
+)
+
+func TestOneByteReader_nonEmptyReader(t *testing.T) {
+ msg := "Hello, World!"
+ buf := new(bytes.Buffer)
+ buf.WriteString(msg)
+
+ obr := OneByteReader(buf)
+ var b []byte
+ n, err := obr.Read(b)
+ if err != nil || n != 0 {
+ t.Errorf("Empty buffer read returned n=%d err=%v", n, err)
+ }
+
+ b = make([]byte, 3)
+ // Read from obr until EOF.
+ got := new(strings.Builder)
+ for i := 0; ; i++ {
+ n, err = obr.Read(b)
+ if err != nil {
+ break
+ }
+ if g, w := n, 1; g != w {
+ t.Errorf("Iteration #%d read %d bytes, want %d", i, g, w)
+ }
+ got.Write(b[:n])
+ }
+ if g, w := err, io.EOF; g != w {
+ t.Errorf("Unexpected error after reading all bytes\n\tGot: %v\n\tWant: %v", g, w)
+ }
+ if g, w := got.String(), "Hello, World!"; g != w {
+ t.Errorf("Read mismatch\n\tGot: %q\n\tWant: %q", g, w)
+ }
+}
+
+func TestOneByteReader_emptyReader(t *testing.T) {
+ r := new(bytes.Buffer)
+
+ obr := OneByteReader(r)
+ var b []byte
+ if n, err := obr.Read(b); err != nil || n != 0 {
+ t.Errorf("Empty buffer read returned n=%d err=%v", n, err)
+ }
+
+ b = make([]byte, 5)
+ n, err := obr.Read(b)
+ if g, w := err, io.EOF; g != w {
+ t.Errorf("Error mismatch\n\tGot: %v\n\tWant: %v", g, w)
+ }
+ if g, w := n, 0; g != w {
+ t.Errorf("Unexpectedly read %d bytes, wanted %d", g, w)
+ }
+}
+
+func TestHalfReader_nonEmptyReader(t *testing.T) {
+ msg := "Hello, World!"
+ buf := new(bytes.Buffer)
+ buf.WriteString(msg)
+ // empty read buffer
+ hr := HalfReader(buf)
+ var b []byte
+ n, err := hr.Read(b)
+ if err != nil || n != 0 {
+ t.Errorf("Empty buffer read returned n=%d err=%v", n, err)
+ }
+ // non empty read buffer
+ b = make([]byte, 2)
+ got := new(strings.Builder)
+ for i := 0; ; i++ {
+ n, err = hr.Read(b)
+ if err != nil {
+ break
+ }
+ if g, w := n, 1; g != w {
+ t.Errorf("Iteration #%d read %d bytes, want %d", i, g, w)
+ }
+ got.Write(b[:n])
+ }
+ if g, w := err, io.EOF; g != w {
+ t.Errorf("Unexpected error after reading all bytes\n\tGot: %v\n\tWant: %v", g, w)
+ }
+ if g, w := got.String(), "Hello, World!"; g != w {
+ t.Errorf("Read mismatch\n\tGot: %q\n\tWant: %q", g, w)
+ }
+}
+
+func TestHalfReader_emptyReader(t *testing.T) {
+ r := new(bytes.Buffer)
+
+ hr := HalfReader(r)
+ var b []byte
+ if n, err := hr.Read(b); err != nil || n != 0 {
+ t.Errorf("Empty buffer read returned n=%d err=%v", n, err)
+ }
+
+ b = make([]byte, 5)
+ n, err := hr.Read(b)
+ if g, w := err, io.EOF; g != w {
+ t.Errorf("Error mismatch\n\tGot: %v\n\tWant: %v", g, w)
+ }
+ if g, w := n, 0; g != w {
+ t.Errorf("Unexpectedly read %d bytes, wanted %d", g, w)
+ }
+}
+
+func TestTimeOutReader_nonEmptyReader(t *testing.T) {
+ msg := "Hello, World!"
+ buf := new(bytes.Buffer)
+ buf.WriteString(msg)
+ // empty read buffer
+ tor := TimeoutReader(buf)
+ var b []byte
+ n, err := tor.Read(b)
+ if err != nil || n != 0 {
+ t.Errorf("Empty buffer read returned n=%d err=%v", n, err)
+ }
+ // Second call should timeout
+ n, err = tor.Read(b)
+ if g, w := err, ErrTimeout; g != w {
+ t.Errorf("Error mismatch\n\tGot: %v\n\tWant: %v", g, w)
+ }
+ if g, w := n, 0; g != w {
+ t.Errorf("Unexpectedly read %d bytes, wanted %d", g, w)
+ }
+ // non empty read buffer
+ tor2 := TimeoutReader(buf)
+ b = make([]byte, 3)
+ if n, err := tor2.Read(b); err != nil || n == 0 {
+ t.Errorf("Empty buffer read returned n=%d err=%v", n, err)
+ }
+ // Second call should timeout
+ n, err = tor2.Read(b)
+ if g, w := err, ErrTimeout; g != w {
+ t.Errorf("Error mismatch\n\tGot: %v\n\tWant: %v", g, w)
+ }
+ if g, w := n, 0; g != w {
+ t.Errorf("Unexpectedly read %d bytes, wanted %d", g, w)
+ }
+}
+
+func TestTimeOutReader_emptyReader(t *testing.T) {
+ r := new(bytes.Buffer)
+ // empty read buffer
+ tor := TimeoutReader(r)
+ var b []byte
+ if n, err := tor.Read(b); err != nil || n != 0 {
+ t.Errorf("Empty buffer read returned n=%d err=%v", n, err)
+ }
+ // Second call should timeout
+ n, err := tor.Read(b)
+ if g, w := err, ErrTimeout; g != w {
+ t.Errorf("Error mismatch\n\tGot: %v\n\tWant: %v", g, w)
+ }
+ if g, w := n, 0; g != w {
+ t.Errorf("Unexpectedly read %d bytes, wanted %d", g, w)
+ }
+ // non empty read buffer
+ tor2 := TimeoutReader(r)
+ b = make([]byte, 5)
+ if n, err := tor2.Read(b); err != io.EOF || n != 0 {
+ t.Errorf("Empty buffer read returned n=%d err=%v", n, err)
+ }
+ // Second call should timeout
+ n, err = tor2.Read(b)
+ if g, w := err, ErrTimeout; g != w {
+ t.Errorf("Error mismatch\n\tGot: %v\n\tWant: %v", g, w)
+ }
+ if g, w := n, 0; g != w {
+ t.Errorf("Unexpectedly read %d bytes, wanted %d", g, w)
+ }
+}
+
+func TestDataErrReader_nonEmptyReader(t *testing.T) {
+ msg := "Hello, World!"
+ buf := new(bytes.Buffer)
+ buf.WriteString(msg)
+
+ der := DataErrReader(buf)
+
+ b := make([]byte, 3)
+ got := new(strings.Builder)
+ var n int
+ var err error
+ for {
+ n, err = der.Read(b)
+ got.Write(b[:n])
+ if err != nil {
+ break
+ }
+ }
+ if err != io.EOF || n == 0 {
+ t.Errorf("Last Read returned n=%d err=%v", n, err)
+ }
+ if g, w := got.String(), "Hello, World!"; g != w {
+ t.Errorf("Read mismatch\n\tGot: %q\n\tWant: %q", g, w)
+ }
+}
+
+func TestDataErrReader_emptyReader(t *testing.T) {
+ r := new(bytes.Buffer)
+
+ der := DataErrReader(r)
+ var b []byte
+ if n, err := der.Read(b); err != io.EOF || n != 0 {
+ t.Errorf("Empty buffer read returned n=%d err=%v", n, err)
+ }
+
+ b = make([]byte, 5)
+ n, err := der.Read(b)
+ if g, w := err, io.EOF; g != w {
+ t.Errorf("Error mismatch\n\tGot: %v\n\tWant: %v", g, w)
+ }
+ if g, w := n, 0; g != w {
+ t.Errorf("Unexpectedly read %d bytes, wanted %d", g, w)
+ }
+}
+
+func TestErrReader(t *testing.T) {
+ cases := []struct {
+ name string
+ err error
+ }{
+ {"nil error", nil},
+ {"non-nil error", errors.New("io failure")},
+ {"io.EOF", io.EOF},
+ }
+
+ for _, tt := range cases {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ n, err := ErrReader(tt.err).Read(nil)
+ if err != tt.err {
+ t.Fatalf("Error mismatch\nGot: %v\nWant: %v", err, tt.err)
+ }
+ if n != 0 {
+ t.Fatalf("Byte count mismatch: got %d want 0", n)
+ }
+ })
+ }
+}
+
+func TestStringsReader(t *testing.T) {
+ const msg = "Now is the time for all good gophers."
+
+ r := strings.NewReader(msg)
+ if err := TestReader(r, []byte(msg)); err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/src/testing/iotest/writer.go b/src/testing/iotest/writer.go
new file mode 100644
index 0000000..af61ab8
--- /dev/null
+++ b/src/testing/iotest/writer.go
@@ -0,0 +1,35 @@
+// Copyright 2009 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 iotest
+
+import "io"
+
+// TruncateWriter returns a Writer that writes to w
+// but stops silently after n bytes.
+func TruncateWriter(w io.Writer, n int64) io.Writer {
+ return &truncateWriter{w, n}
+}
+
+type truncateWriter struct {
+ w io.Writer
+ n int64
+}
+
+func (t *truncateWriter) Write(p []byte) (n int, err error) {
+ if t.n <= 0 {
+ return len(p), nil
+ }
+ // real write
+ n = len(p)
+ if int64(n) > t.n {
+ n = int(t.n)
+ }
+ n, err = t.w.Write(p[0:n])
+ t.n -= int64(n)
+ if err == nil {
+ n = len(p)
+ }
+ return
+}
diff --git a/src/testing/iotest/writer_test.go b/src/testing/iotest/writer_test.go
new file mode 100644
index 0000000..2762513
--- /dev/null
+++ b/src/testing/iotest/writer_test.go
@@ -0,0 +1,39 @@
+// Copyright 2019 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 iotest
+
+import (
+ "strings"
+ "testing"
+)
+
+var truncateWriterTests = []struct {
+ in string
+ want string
+ trunc int64
+ n int
+}{
+ {"hello", "", -1, 5},
+ {"world", "", 0, 5},
+ {"abcde", "abc", 3, 5},
+ {"edcba", "edcba", 7, 5},
+}
+
+func TestTruncateWriter(t *testing.T) {
+ for _, tt := range truncateWriterTests {
+ buf := new(strings.Builder)
+ tw := TruncateWriter(buf, tt.trunc)
+ n, err := tw.Write([]byte(tt.in))
+ if err != nil {
+ t.Errorf("Unexpected error %v for\n\t%+v", err, tt)
+ }
+ if g, w := buf.String(), tt.want; g != w {
+ t.Errorf("got %q, expected %q", g, w)
+ }
+ if g, w := n, tt.n; g != w {
+ t.Errorf("read %d bytes, but expected to have read %d bytes for\n\t%+v", g, w, tt)
+ }
+ }
+}
diff --git a/src/testing/match.go b/src/testing/match.go
new file mode 100644
index 0000000..84804dc
--- /dev/null
+++ b/src/testing/match.go
@@ -0,0 +1,317 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testing
+
+import (
+ "fmt"
+ "os"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+// matcher sanitizes, uniques, and filters names of subtests and subbenchmarks.
+type matcher struct {
+ filter filterMatch
+ skip filterMatch
+ matchFunc func(pat, str string) (bool, error)
+
+ mu sync.Mutex
+
+ // subNames is used to deduplicate subtest names.
+ // Each key is the subtest name joined to the deduplicated name of the parent test.
+ // Each value is the count of the number of occurrences of the given subtest name
+ // already seen.
+ subNames map[string]int32
+}
+
+type filterMatch interface {
+ // matches checks the name against the receiver's pattern strings using the
+ // given match function.
+ matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool)
+
+ // verify checks that the receiver's pattern strings are valid filters by
+ // calling the given match function.
+ verify(name string, matchString func(pat, str string) (bool, error)) error
+}
+
+// simpleMatch matches a test name if all of the pattern strings match in
+// sequence.
+type simpleMatch []string
+
+// alternationMatch matches a test name if one of the alternations match.
+type alternationMatch []filterMatch
+
+// TODO: fix test_main to avoid race and improve caching, also allowing to
+// eliminate this Mutex.
+var matchMutex sync.Mutex
+
+func allMatcher() *matcher {
+ return newMatcher(nil, "", "", "")
+}
+
+func newMatcher(matchString func(pat, str string) (bool, error), patterns, name, skips string) *matcher {
+ var filter, skip filterMatch
+ if patterns == "" {
+ filter = simpleMatch{} // always partial true
+ } else {
+ filter = splitRegexp(patterns)
+ if err := filter.verify(name, matchString); err != nil {
+ fmt.Fprintf(os.Stderr, "testing: invalid regexp for %s\n", err)
+ os.Exit(1)
+ }
+ }
+ if skips == "" {
+ skip = alternationMatch{} // always false
+ } else {
+ skip = splitRegexp(skips)
+ if err := skip.verify("-test.skip", matchString); err != nil {
+ fmt.Fprintf(os.Stderr, "testing: invalid regexp for %v\n", err)
+ os.Exit(1)
+ }
+ }
+ return &matcher{
+ filter: filter,
+ skip: skip,
+ matchFunc: matchString,
+ subNames: map[string]int32{},
+ }
+}
+
+func (m *matcher) fullName(c *common, subname string) (name string, ok, partial bool) {
+ name = subname
+
+ m.mu.Lock()
+ defer m.mu.Unlock()
+
+ if c != nil && c.level > 0 {
+ name = m.unique(c.name, rewrite(subname))
+ }
+
+ matchMutex.Lock()
+ defer matchMutex.Unlock()
+
+ // We check the full array of paths each time to allow for the case that a pattern contains a '/'.
+ elem := strings.Split(name, "/")
+
+ // filter must match.
+ // accept partial match that may produce full match later.
+ ok, partial = m.filter.matches(elem, m.matchFunc)
+ if !ok {
+ return name, false, false
+ }
+
+ // skip must not match.
+ // ignore partial match so we can get to more precise match later.
+ skip, partialSkip := m.skip.matches(elem, m.matchFunc)
+ if skip && !partialSkip {
+ return name, false, false
+ }
+
+ return name, ok, partial
+}
+
+// clearSubNames clears the matcher's internal state, potentially freeing
+// memory. After this is called, T.Name may return the same strings as it did
+// for earlier subtests.
+func (m *matcher) clearSubNames() {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ clear(m.subNames)
+}
+
+func (m simpleMatch) matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool) {
+ for i, s := range name {
+ if i >= len(m) {
+ break
+ }
+ if ok, _ := matchString(m[i], s); !ok {
+ return false, false
+ }
+ }
+ return true, len(name) < len(m)
+}
+
+func (m simpleMatch) verify(name string, matchString func(pat, str string) (bool, error)) error {
+ for i, s := range m {
+ m[i] = rewrite(s)
+ }
+ // Verify filters before doing any processing.
+ for i, s := range m {
+ if _, err := matchString(s, "non-empty"); err != nil {
+ return fmt.Errorf("element %d of %s (%q): %s", i, name, s, err)
+ }
+ }
+ return nil
+}
+
+func (m alternationMatch) matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool) {
+ for _, m := range m {
+ if ok, partial = m.matches(name, matchString); ok {
+ return ok, partial
+ }
+ }
+ return false, false
+}
+
+func (m alternationMatch) verify(name string, matchString func(pat, str string) (bool, error)) error {
+ for i, m := range m {
+ if err := m.verify(name, matchString); err != nil {
+ return fmt.Errorf("alternation %d of %s", i, err)
+ }
+ }
+ return nil
+}
+
+func splitRegexp(s string) filterMatch {
+ a := make(simpleMatch, 0, strings.Count(s, "/"))
+ b := make(alternationMatch, 0, strings.Count(s, "|"))
+ cs := 0
+ cp := 0
+ for i := 0; i < len(s); {
+ switch s[i] {
+ case '[':
+ cs++
+ case ']':
+ if cs--; cs < 0 { // An unmatched ']' is legal.
+ cs = 0
+ }
+ case '(':
+ if cs == 0 {
+ cp++
+ }
+ case ')':
+ if cs == 0 {
+ cp--
+ }
+ case '\\':
+ i++
+ case '/':
+ if cs == 0 && cp == 0 {
+ a = append(a, s[:i])
+ s = s[i+1:]
+ i = 0
+ continue
+ }
+ case '|':
+ if cs == 0 && cp == 0 {
+ a = append(a, s[:i])
+ s = s[i+1:]
+ i = 0
+ b = append(b, a)
+ a = make(simpleMatch, 0, len(a))
+ continue
+ }
+ }
+ i++
+ }
+
+ a = append(a, s)
+ if len(b) == 0 {
+ return a
+ }
+ return append(b, a)
+}
+
+// unique creates a unique name for the given parent and subname by affixing it
+// with one or more counts, if necessary.
+func (m *matcher) unique(parent, subname string) string {
+ base := parent + "/" + subname
+
+ for {
+ n := m.subNames[base]
+ if n < 0 {
+ panic("subtest count overflow")
+ }
+ m.subNames[base] = n + 1
+
+ if n == 0 && subname != "" {
+ prefix, nn := parseSubtestNumber(base)
+ if len(prefix) < len(base) && nn < m.subNames[prefix] {
+ // This test is explicitly named like "parent/subname#NN",
+ // and #NN was already used for the NNth occurrence of "parent/subname".
+ // Loop to add a disambiguating suffix.
+ continue
+ }
+ return base
+ }
+
+ name := fmt.Sprintf("%s#%02d", base, n)
+ if m.subNames[name] != 0 {
+ // This is the nth occurrence of base, but the name "parent/subname#NN"
+ // collides with the first occurrence of a subtest *explicitly* named
+ // "parent/subname#NN". Try the next number.
+ continue
+ }
+
+ return name
+ }
+}
+
+// parseSubtestNumber splits a subtest name into a "#%02d"-formatted int32
+// suffix (if present), and a prefix preceding that suffix (always).
+func parseSubtestNumber(s string) (prefix string, nn int32) {
+ i := strings.LastIndex(s, "#")
+ if i < 0 {
+ return s, 0
+ }
+
+ prefix, suffix := s[:i], s[i+1:]
+ if len(suffix) < 2 || (len(suffix) > 2 && suffix[0] == '0') {
+ // Even if suffix is numeric, it is not a possible output of a "%02" format
+ // string: it has either too few digits or too many leading zeroes.
+ return s, 0
+ }
+ if suffix == "00" {
+ if !strings.HasSuffix(prefix, "/") {
+ // We only use "#00" as a suffix for subtests named with the empty
+ // string — it isn't a valid suffix if the subtest name is non-empty.
+ return s, 0
+ }
+ }
+
+ n, err := strconv.ParseInt(suffix, 10, 32)
+ if err != nil || n < 0 {
+ return s, 0
+ }
+ return prefix, int32(n)
+}
+
+// rewrite rewrites a subname to having only printable characters and no white
+// space.
+func rewrite(s string) string {
+ b := []byte{}
+ for _, r := range s {
+ switch {
+ case isSpace(r):
+ b = append(b, '_')
+ case !strconv.IsPrint(r):
+ s := strconv.QuoteRune(r)
+ b = append(b, s[1:len(s)-1]...)
+ default:
+ b = append(b, string(r)...)
+ }
+ }
+ return string(b)
+}
+
+func isSpace(r rune) bool {
+ if r < 0x2000 {
+ switch r {
+ // Note: not the same as Unicode Z class.
+ case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0, 0x1680:
+ return true
+ }
+ } else {
+ if r <= 0x200a {
+ return true
+ }
+ switch r {
+ case 0x2028, 0x2029, 0x202f, 0x205f, 0x3000:
+ return true
+ }
+ }
+ return false
+}
diff --git a/src/testing/match_test.go b/src/testing/match_test.go
new file mode 100644
index 0000000..d31efbc
--- /dev/null
+++ b/src/testing/match_test.go
@@ -0,0 +1,263 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testing
+
+import (
+ "fmt"
+ "reflect"
+ "regexp"
+ "strings"
+ "unicode"
+)
+
+func init() {
+ testingTesting = true
+}
+
+// Verify that our IsSpace agrees with unicode.IsSpace.
+func TestIsSpace(t *T) {
+ n := 0
+ for r := rune(0); r <= unicode.MaxRune; r++ {
+ if isSpace(r) != unicode.IsSpace(r) {
+ t.Errorf("IsSpace(%U)=%t incorrect", r, isSpace(r))
+ n++
+ if n > 10 {
+ return
+ }
+ }
+ }
+}
+
+func TestSplitRegexp(t *T) {
+ res := func(s ...string) filterMatch { return simpleMatch(s) }
+ alt := func(m ...filterMatch) filterMatch { return alternationMatch(m) }
+ testCases := []struct {
+ pattern string
+ result filterMatch
+ }{
+ // Correct patterns
+ // If a regexp pattern is correct, all split regexps need to be correct
+ // as well.
+ {"", res("")},
+ {"/", res("", "")},
+ {"//", res("", "", "")},
+ {"A", res("A")},
+ {"A/B", res("A", "B")},
+ {"A/B/", res("A", "B", "")},
+ {"/A/B/", res("", "A", "B", "")},
+ {"[A]/(B)", res("[A]", "(B)")},
+ {"[/]/[/]", res("[/]", "[/]")},
+ {"[/]/[:/]", res("[/]", "[:/]")},
+ {"/]", res("", "]")},
+ {"]/", res("]", "")},
+ {"]/[/]", res("]", "[/]")},
+ {`([)/][(])`, res(`([)/][(])`)},
+ {"[(]/[)]", res("[(]", "[)]")},
+
+ {"A/B|C/D", alt(res("A", "B"), res("C", "D"))},
+
+ // Faulty patterns
+ // Errors in original should produce at least one faulty regexp in results.
+ {")/", res(")/")},
+ {")/(/)", res(")/(", ")")},
+ {"a[/)b", res("a[/)b")},
+ {"(/]", res("(/]")},
+ {"(/", res("(/")},
+ {"[/]/[/", res("[/]", "[/")},
+ {`\p{/}`, res(`\p{`, "}")},
+ {`\p/`, res(`\p`, "")},
+ {`[[:/:]]`, res(`[[:/:]]`)},
+ }
+ for _, tc := range testCases {
+ a := splitRegexp(tc.pattern)
+ if !reflect.DeepEqual(a, tc.result) {
+ t.Errorf("splitRegexp(%q) = %#v; want %#v", tc.pattern, a, tc.result)
+ }
+
+ // If there is any error in the pattern, one of the returned subpatterns
+ // needs to have an error as well.
+ if _, err := regexp.Compile(tc.pattern); err != nil {
+ ok := true
+ if err := a.verify("", regexp.MatchString); err != nil {
+ ok = false
+ }
+ if ok {
+ t.Errorf("%s: expected error in any of %q", tc.pattern, a)
+ }
+ }
+ }
+}
+
+func TestMatcher(t *T) {
+ testCases := []struct {
+ pattern string
+ skip string
+ parent, sub string
+ ok bool
+ partial bool
+ }{
+ // Behavior without subtests.
+ {"", "", "", "TestFoo", true, false},
+ {"TestFoo", "", "", "TestFoo", true, false},
+ {"TestFoo/", "", "", "TestFoo", true, true},
+ {"TestFoo/bar/baz", "", "", "TestFoo", true, true},
+ {"TestFoo", "", "", "TestBar", false, false},
+ {"TestFoo/", "", "", "TestBar", false, false},
+ {"TestFoo/bar/baz", "", "", "TestBar/bar/baz", false, false},
+ {"", "TestBar", "", "TestFoo", true, false},
+ {"", "TestBar", "", "TestBar", false, false},
+
+ // Skipping a non-existent test doesn't change anything.
+ {"", "TestFoo/skipped", "", "TestFoo", true, false},
+ {"TestFoo", "TestFoo/skipped", "", "TestFoo", true, false},
+ {"TestFoo/", "TestFoo/skipped", "", "TestFoo", true, true},
+ {"TestFoo/bar/baz", "TestFoo/skipped", "", "TestFoo", true, true},
+ {"TestFoo", "TestFoo/skipped", "", "TestBar", false, false},
+ {"TestFoo/", "TestFoo/skipped", "", "TestBar", false, false},
+ {"TestFoo/bar/baz", "TestFoo/skipped", "", "TestBar/bar/baz", false, false},
+
+ // with subtests
+ {"", "", "TestFoo", "x", true, false},
+ {"TestFoo", "", "TestFoo", "x", true, false},
+ {"TestFoo/", "", "TestFoo", "x", true, false},
+ {"TestFoo/bar/baz", "", "TestFoo", "bar", true, true},
+
+ {"", "TestFoo/skipped", "TestFoo", "x", true, false},
+ {"TestFoo", "TestFoo/skipped", "TestFoo", "x", true, false},
+ {"TestFoo", "TestFoo/skipped", "TestFoo", "skipped", false, false},
+ {"TestFoo/", "TestFoo/skipped", "TestFoo", "x", true, false},
+ {"TestFoo/bar/baz", "TestFoo/skipped", "TestFoo", "bar", true, true},
+
+ // Subtest with a '/' in its name still allows for copy and pasted names
+ // to match.
+ {"TestFoo/bar/baz", "", "TestFoo", "bar/baz", true, false},
+ {"TestFoo/bar/baz", "TestFoo/bar/baz", "TestFoo", "bar/baz", false, false},
+ {"TestFoo/bar/baz", "TestFoo/bar/baz/skip", "TestFoo", "bar/baz", true, false},
+ {"TestFoo/bar/baz", "", "TestFoo/bar", "baz", true, false},
+ {"TestFoo/bar/baz", "", "TestFoo", "x", false, false},
+ {"TestFoo", "", "TestBar", "x", false, false},
+ {"TestFoo/", "", "TestBar", "x", false, false},
+ {"TestFoo/bar/baz", "", "TestBar", "x/bar/baz", false, false},
+
+ {"A/B|C/D", "", "TestA", "B", true, false},
+ {"A/B|C/D", "", "TestC", "D", true, false},
+ {"A/B|C/D", "", "TestA", "C", false, false},
+
+ // subtests only
+ {"", "", "TestFoo", "x", true, false},
+ {"/", "", "TestFoo", "x", true, false},
+ {"./", "", "TestFoo", "x", true, false},
+ {"./.", "", "TestFoo", "x", true, false},
+ {"/bar/baz", "", "TestFoo", "bar", true, true},
+ {"/bar/baz", "", "TestFoo", "bar/baz", true, false},
+ {"//baz", "", "TestFoo", "bar/baz", true, false},
+ {"//", "", "TestFoo", "bar/baz", true, false},
+ {"/bar/baz", "", "TestFoo/bar", "baz", true, false},
+ {"//foo", "", "TestFoo", "bar/baz", false, false},
+ {"/bar/baz", "", "TestFoo", "x", false, false},
+ {"/bar/baz", "", "TestBar", "x/bar/baz", false, false},
+ }
+
+ for _, tc := range testCases {
+ m := newMatcher(regexp.MatchString, tc.pattern, "-test.run", tc.skip)
+
+ parent := &common{name: tc.parent}
+ if tc.parent != "" {
+ parent.level = 1
+ }
+ if n, ok, partial := m.fullName(parent, tc.sub); ok != tc.ok || partial != tc.partial {
+ t.Errorf("for pattern %q, fullName(parent=%q, sub=%q) = %q, ok %v partial %v; want ok %v partial %v",
+ tc.pattern, tc.parent, tc.sub, n, ok, partial, tc.ok, tc.partial)
+ }
+ }
+}
+
+var namingTestCases = []struct{ name, want string }{
+ // Uniqueness
+ {"", "x/#00"},
+ {"", "x/#01"},
+ {"#0", "x/#0"}, // Doesn't conflict with #00 because the number of digits differs.
+ {"#00", "x/#00#01"}, // Conflicts with implicit #00 (used above), so add a suffix.
+ {"#", "x/#"},
+ {"#", "x/##01"},
+
+ {"t", "x/t"},
+ {"t", "x/t#01"},
+ {"t", "x/t#02"},
+ {"t#00", "x/t#00"}, // Explicit "#00" doesn't conflict with the unsuffixed first subtest.
+
+ {"a#01", "x/a#01"}, // user has subtest with this name.
+ {"a", "x/a"}, // doesn't conflict with this name.
+ {"a", "x/a#02"}, // This string is claimed now, so resume
+ {"a", "x/a#03"}, // with counting.
+ {"a#02", "x/a#02#01"}, // We already used a#02 once, so add a suffix.
+
+ {"b#00", "x/b#00"},
+ {"b", "x/b"}, // Implicit 0 doesn't conflict with explicit "#00".
+ {"b", "x/b#01"},
+ {"b#9223372036854775807", "x/b#9223372036854775807"}, // MaxInt64
+ {"b", "x/b#02"},
+ {"b", "x/b#03"},
+
+ // Sanitizing
+ {"A:1 B:2", "x/A:1_B:2"},
+ {"s\t\r\u00a0", "x/s___"},
+ {"\x01", `x/\x01`},
+ {"\U0010ffff", `x/\U0010ffff`},
+}
+
+func TestNaming(t *T) {
+ m := newMatcher(regexp.MatchString, "", "", "")
+ parent := &common{name: "x", level: 1} // top-level test.
+
+ for i, tc := range namingTestCases {
+ if got, _, _ := m.fullName(parent, tc.name); got != tc.want {
+ t.Errorf("%d:%s: got %q; want %q", i, tc.name, got, tc.want)
+ }
+ }
+}
+
+func FuzzNaming(f *F) {
+ for _, tc := range namingTestCases {
+ f.Add(tc.name)
+ }
+ parent := &common{name: "x", level: 1}
+ var m *matcher
+ var seen map[string]string
+ reset := func() {
+ m = allMatcher()
+ seen = make(map[string]string)
+ }
+ reset()
+
+ f.Fuzz(func(t *T, subname string) {
+ if len(subname) > 10 {
+ // Long names attract the OOM killer.
+ t.Skip()
+ }
+ name := m.unique(parent.name, subname)
+ if !strings.Contains(name, "/"+subname) {
+ t.Errorf("name %q does not contain subname %q", name, subname)
+ }
+ if prev, ok := seen[name]; ok {
+ t.Errorf("name %q generated by both %q and %q", name, prev, subname)
+ }
+ if len(seen) > 1e6 {
+ // Free up memory.
+ reset()
+ }
+ seen[name] = subname
+ })
+}
+
+// GoString returns a string that is more readable than the default, which makes
+// it easier to read test errors.
+func (m alternationMatch) GoString() string {
+ s := make([]string, len(m))
+ for i, m := range m {
+ s[i] = fmt.Sprintf("%#v", m)
+ }
+ return fmt.Sprintf("(%s)", strings.Join(s, " | "))
+}
diff --git a/src/testing/newcover.go b/src/testing/newcover.go
new file mode 100644
index 0000000..6199f3b
--- /dev/null
+++ b/src/testing/newcover.go
@@ -0,0 +1,59 @@
+// Copyright 2022 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.
+
+// Support for test coverage with redesigned coverage implementation.
+
+package testing
+
+import (
+ "fmt"
+ "internal/goexperiment"
+ "os"
+)
+
+// cover2 variable stores the current coverage mode and a
+// tear-down function to be called at the end of the testing run.
+var cover2 struct {
+ mode string
+ tearDown func(coverprofile string, gocoverdir string) (string, error)
+ snapshotcov func() float64
+}
+
+// registerCover2 is invoked during "go test -cover" runs by the test harness
+// code in _testmain.go; it is used to record a 'tear down' function
+// (to be called when the test is complete) and the coverage mode.
+func registerCover2(mode string, tearDown func(coverprofile string, gocoverdir string) (string, error), snapcov func() float64) {
+ cover2.mode = mode
+ cover2.tearDown = tearDown
+ cover2.snapshotcov = snapcov
+}
+
+// coverReport2 invokes a callback in _testmain.go that will
+// emit coverage data at the point where test execution is complete,
+// for "go test -cover" runs.
+func coverReport2() {
+ if !goexperiment.CoverageRedesign {
+ panic("unexpected")
+ }
+ if errmsg, err := cover2.tearDown(*coverProfile, *gocoverdir); err != nil {
+ fmt.Fprintf(os.Stderr, "%s: %v\n", errmsg, err)
+ os.Exit(2)
+ }
+}
+
+// testGoCoverDir returns the value passed to the -test.gocoverdir
+// flag by the Go command, if goexperiment.CoverageRedesign is
+// in effect.
+func testGoCoverDir() string {
+ return *gocoverdir
+}
+
+// coverage2 returns a rough "coverage percentage so far"
+// number to support the testing.Coverage() function.
+func coverage2() float64 {
+ if cover2.mode == "" {
+ return 0.0
+ }
+ return cover2.snapshotcov()
+}
diff --git a/src/testing/panic_test.go b/src/testing/panic_test.go
new file mode 100644
index 0000000..6307b84
--- /dev/null
+++ b/src/testing/panic_test.go
@@ -0,0 +1,267 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testing_test
+
+import (
+ "flag"
+ "fmt"
+ "internal/testenv"
+ "os"
+ "os/exec"
+ "regexp"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+var testPanicTest = flag.String("test_panic_test", "", "TestPanic: indicates which test should panic")
+var testPanicParallel = flag.Bool("test_panic_parallel", false, "TestPanic: run subtests in parallel")
+var testPanicCleanup = flag.Bool("test_panic_cleanup", false, "TestPanic: indicates whether test should call Cleanup")
+var testPanicCleanupPanic = flag.String("test_panic_cleanup_panic", "", "TestPanic: indicate whether test should call Cleanup function that panics")
+
+func TestPanic(t *testing.T) {
+ testenv.MustHaveExec(t)
+
+ testCases := []struct {
+ desc string
+ flags []string
+ want string
+ }{{
+ desc: "root test panics",
+ flags: []string{"-test_panic_test=TestPanicHelper"},
+ want: `
+--- FAIL: TestPanicHelper (N.NNs)
+ panic_test.go:NNN: TestPanicHelper
+`,
+ }, {
+ desc: "subtest panics",
+ flags: []string{"-test_panic_test=TestPanicHelper/1"},
+ want: `
+--- FAIL: TestPanicHelper (N.NNs)
+ panic_test.go:NNN: TestPanicHelper
+ --- FAIL: TestPanicHelper/1 (N.NNs)
+ panic_test.go:NNN: TestPanicHelper/1
+`,
+ }, {
+ desc: "subtest panics with cleanup",
+ flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup"},
+ want: `
+ran inner cleanup 1
+ran middle cleanup 1
+ran outer cleanup
+--- FAIL: TestPanicHelper (N.NNs)
+ panic_test.go:NNN: TestPanicHelper
+ --- FAIL: TestPanicHelper/1 (N.NNs)
+ panic_test.go:NNN: TestPanicHelper/1
+`,
+ }, {
+ desc: "subtest panics with outer cleanup panic",
+ flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=outer"},
+ want: `
+ran inner cleanup 1
+ran middle cleanup 1
+ran outer cleanup
+--- FAIL: TestPanicHelper (N.NNs)
+ panic_test.go:NNN: TestPanicHelper
+`,
+ }, {
+ desc: "subtest panics with middle cleanup panic",
+ flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=middle"},
+ want: `
+ran inner cleanup 1
+ran middle cleanup 1
+ran outer cleanup
+--- FAIL: TestPanicHelper (N.NNs)
+ panic_test.go:NNN: TestPanicHelper
+ --- FAIL: TestPanicHelper/1 (N.NNs)
+ panic_test.go:NNN: TestPanicHelper/1
+`,
+ }, {
+ desc: "subtest panics with inner cleanup panic",
+ flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=inner"},
+ want: `
+ran inner cleanup 1
+ran middle cleanup 1
+ran outer cleanup
+--- FAIL: TestPanicHelper (N.NNs)
+ panic_test.go:NNN: TestPanicHelper
+ --- FAIL: TestPanicHelper/1 (N.NNs)
+ panic_test.go:NNN: TestPanicHelper/1
+`,
+ }, {
+ desc: "parallel subtest panics with cleanup",
+ flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_parallel"},
+ want: `
+ran inner cleanup 1
+ran middle cleanup 1
+ran outer cleanup
+--- FAIL: TestPanicHelper (N.NNs)
+ panic_test.go:NNN: TestPanicHelper
+ --- FAIL: TestPanicHelper/1 (N.NNs)
+ panic_test.go:NNN: TestPanicHelper/1
+`,
+ }, {
+ desc: "parallel subtest panics with outer cleanup panic",
+ flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=outer", "-test_panic_parallel"},
+ want: `
+ran inner cleanup 1
+ran middle cleanup 1
+ran outer cleanup
+--- FAIL: TestPanicHelper (N.NNs)
+ panic_test.go:NNN: TestPanicHelper
+`,
+ }, {
+ desc: "parallel subtest panics with middle cleanup panic",
+ flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=middle", "-test_panic_parallel"},
+ want: `
+ran inner cleanup 1
+ran middle cleanup 1
+ran outer cleanup
+--- FAIL: TestPanicHelper (N.NNs)
+ panic_test.go:NNN: TestPanicHelper
+ --- FAIL: TestPanicHelper/1 (N.NNs)
+ panic_test.go:NNN: TestPanicHelper/1
+`,
+ }, {
+ desc: "parallel subtest panics with inner cleanup panic",
+ flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=inner", "-test_panic_parallel"},
+ want: `
+ran inner cleanup 1
+ran middle cleanup 1
+ran outer cleanup
+--- FAIL: TestPanicHelper (N.NNs)
+ panic_test.go:NNN: TestPanicHelper
+ --- FAIL: TestPanicHelper/1 (N.NNs)
+ panic_test.go:NNN: TestPanicHelper/1
+`,
+ }}
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ cmd := exec.Command(os.Args[0], "-test.run=^TestPanicHelper$")
+ cmd.Args = append(cmd.Args, tc.flags...)
+ cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
+ b, _ := cmd.CombinedOutput()
+ got := string(b)
+ want := strings.TrimSpace(tc.want)
+ re := makeRegexp(want)
+ if ok, err := regexp.MatchString(re, got); !ok || err != nil {
+ t.Errorf("output:\ngot:\n%s\nwant:\n%s", got, want)
+ }
+ })
+ }
+}
+
+func makeRegexp(s string) string {
+ s = regexp.QuoteMeta(s)
+ s = strings.ReplaceAll(s, ":NNN:", `:\d+:`)
+ s = strings.ReplaceAll(s, "N\\.NNs", `\d*\.\d*s`)
+ return s
+}
+
+func TestPanicHelper(t *testing.T) {
+ if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
+ return
+ }
+ t.Log(t.Name())
+ if t.Name() == *testPanicTest {
+ panic("panic")
+ }
+ switch *testPanicCleanupPanic {
+ case "", "outer", "middle", "inner":
+ default:
+ t.Fatalf("bad -test_panic_cleanup_panic: %s", *testPanicCleanupPanic)
+ }
+ t.Cleanup(func() {
+ fmt.Println("ran outer cleanup")
+ if *testPanicCleanupPanic == "outer" {
+ panic("outer cleanup")
+ }
+ })
+ for i := 0; i < 3; i++ {
+ i := i
+ t.Run(fmt.Sprintf("%v", i), func(t *testing.T) {
+ chosen := t.Name() == *testPanicTest
+ if chosen && *testPanicCleanup {
+ t.Cleanup(func() {
+ fmt.Printf("ran middle cleanup %d\n", i)
+ if *testPanicCleanupPanic == "middle" {
+ panic("middle cleanup")
+ }
+ })
+ }
+ if chosen && *testPanicParallel {
+ t.Parallel()
+ }
+ t.Log(t.Name())
+ if chosen {
+ if *testPanicCleanup {
+ t.Cleanup(func() {
+ fmt.Printf("ran inner cleanup %d\n", i)
+ if *testPanicCleanupPanic == "inner" {
+ panic("inner cleanup")
+ }
+ })
+ }
+ panic("panic")
+ }
+ })
+ }
+}
+
+func TestMorePanic(t *testing.T) {
+ testenv.MustHaveExec(t)
+
+ testCases := []struct {
+ desc string
+ flags []string
+ want string
+ }{
+ {
+ desc: "Issue 48502: call runtime.Goexit in t.Cleanup after panic",
+ flags: []string{"-test.run=^TestGoexitInCleanupAfterPanicHelper$"},
+ want: `panic: die
+ panic: test executed panic(nil) or runtime.Goexit`,
+ },
+ {
+ desc: "Issue 48515: call t.Run in t.Cleanup should trigger panic",
+ flags: []string{"-test.run=^TestCallRunInCleanupHelper$"},
+ want: `panic: testing: t.Run called during t.Cleanup`,
+ },
+ }
+
+ for _, tc := range testCases {
+ cmd := exec.Command(os.Args[0], tc.flags...)
+ cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
+ b, _ := cmd.CombinedOutput()
+ got := string(b)
+ want := tc.want
+ re := makeRegexp(want)
+ if ok, err := regexp.MatchString(re, got); !ok || err != nil {
+ t.Errorf("output:\ngot:\n%s\nwant:\n%s", got, want)
+ }
+ }
+}
+
+func TestCallRunInCleanupHelper(t *testing.T) {
+ if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
+ return
+ }
+
+ t.Cleanup(func() {
+ t.Run("in-cleanup", func(t *testing.T) {
+ t.Log("must not be executed")
+ })
+ })
+}
+
+func TestGoexitInCleanupAfterPanicHelper(t *testing.T) {
+ if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
+ return
+ }
+
+ t.Cleanup(func() { runtime.Goexit() })
+ t.Parallel()
+ panic("die")
+}
diff --git a/src/testing/quick/quick.go b/src/testing/quick/quick.go
new file mode 100644
index 0000000..8ef9cf7
--- /dev/null
+++ b/src/testing/quick/quick.go
@@ -0,0 +1,385 @@
+// Copyright 2009 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 quick implements utility functions to help with black box testing.
+//
+// The testing/quick package is frozen and is not accepting new features.
+package quick
+
+import (
+ "flag"
+ "fmt"
+ "math"
+ "math/rand"
+ "reflect"
+ "strings"
+ "time"
+)
+
+var defaultMaxCount *int = flag.Int("quickchecks", 100, "The default number of iterations for each check")
+
+// A Generator can generate random values of its own type.
+type Generator interface {
+ // Generate returns a random instance of the type on which it is a
+ // method using the size as a size hint.
+ Generate(rand *rand.Rand, size int) reflect.Value
+}
+
+// randFloat32 generates a random float taking the full range of a float32.
+func randFloat32(rand *rand.Rand) float32 {
+ f := rand.Float64() * math.MaxFloat32
+ if rand.Int()&1 == 1 {
+ f = -f
+ }
+ return float32(f)
+}
+
+// randFloat64 generates a random float taking the full range of a float64.
+func randFloat64(rand *rand.Rand) float64 {
+ f := rand.Float64() * math.MaxFloat64
+ if rand.Int()&1 == 1 {
+ f = -f
+ }
+ return f
+}
+
+// randInt64 returns a random int64.
+func randInt64(rand *rand.Rand) int64 {
+ return int64(rand.Uint64())
+}
+
+// complexSize is the maximum length of arbitrary values that contain other
+// values.
+const complexSize = 50
+
+// Value returns an arbitrary value of the given type.
+// If the type implements the [Generator] interface, that will be used.
+// Note: To create arbitrary values for structs, all the fields must be exported.
+func Value(t reflect.Type, rand *rand.Rand) (value reflect.Value, ok bool) {
+ return sizedValue(t, rand, complexSize)
+}
+
+// sizedValue returns an arbitrary value of the given type. The size
+// hint is used for shrinking as a function of indirection level so
+// that recursive data structures will terminate.
+func sizedValue(t reflect.Type, rand *rand.Rand, size int) (value reflect.Value, ok bool) {
+ if m, ok := reflect.Zero(t).Interface().(Generator); ok {
+ return m.Generate(rand, size), true
+ }
+
+ v := reflect.New(t).Elem()
+ switch concrete := t; concrete.Kind() {
+ case reflect.Bool:
+ v.SetBool(rand.Int()&1 == 0)
+ case reflect.Float32:
+ v.SetFloat(float64(randFloat32(rand)))
+ case reflect.Float64:
+ v.SetFloat(randFloat64(rand))
+ case reflect.Complex64:
+ v.SetComplex(complex(float64(randFloat32(rand)), float64(randFloat32(rand))))
+ case reflect.Complex128:
+ v.SetComplex(complex(randFloat64(rand), randFloat64(rand)))
+ case reflect.Int16:
+ v.SetInt(randInt64(rand))
+ case reflect.Int32:
+ v.SetInt(randInt64(rand))
+ case reflect.Int64:
+ v.SetInt(randInt64(rand))
+ case reflect.Int8:
+ v.SetInt(randInt64(rand))
+ case reflect.Int:
+ v.SetInt(randInt64(rand))
+ case reflect.Uint16:
+ v.SetUint(uint64(randInt64(rand)))
+ case reflect.Uint32:
+ v.SetUint(uint64(randInt64(rand)))
+ case reflect.Uint64:
+ v.SetUint(uint64(randInt64(rand)))
+ case reflect.Uint8:
+ v.SetUint(uint64(randInt64(rand)))
+ case reflect.Uint:
+ v.SetUint(uint64(randInt64(rand)))
+ case reflect.Uintptr:
+ v.SetUint(uint64(randInt64(rand)))
+ case reflect.Map:
+ numElems := rand.Intn(size)
+ v.Set(reflect.MakeMap(concrete))
+ for i := 0; i < numElems; i++ {
+ key, ok1 := sizedValue(concrete.Key(), rand, size)
+ value, ok2 := sizedValue(concrete.Elem(), rand, size)
+ if !ok1 || !ok2 {
+ return reflect.Value{}, false
+ }
+ v.SetMapIndex(key, value)
+ }
+ case reflect.Pointer:
+ if rand.Intn(size) == 0 {
+ v.SetZero() // Generate nil pointer.
+ } else {
+ elem, ok := sizedValue(concrete.Elem(), rand, size)
+ if !ok {
+ return reflect.Value{}, false
+ }
+ v.Set(reflect.New(concrete.Elem()))
+ v.Elem().Set(elem)
+ }
+ case reflect.Slice:
+ numElems := rand.Intn(size)
+ sizeLeft := size - numElems
+ v.Set(reflect.MakeSlice(concrete, numElems, numElems))
+ for i := 0; i < numElems; i++ {
+ elem, ok := sizedValue(concrete.Elem(), rand, sizeLeft)
+ if !ok {
+ return reflect.Value{}, false
+ }
+ v.Index(i).Set(elem)
+ }
+ case reflect.Array:
+ for i := 0; i < v.Len(); i++ {
+ elem, ok := sizedValue(concrete.Elem(), rand, size)
+ if !ok {
+ return reflect.Value{}, false
+ }
+ v.Index(i).Set(elem)
+ }
+ case reflect.String:
+ numChars := rand.Intn(complexSize)
+ codePoints := make([]rune, numChars)
+ for i := 0; i < numChars; i++ {
+ codePoints[i] = rune(rand.Intn(0x10ffff))
+ }
+ v.SetString(string(codePoints))
+ case reflect.Struct:
+ n := v.NumField()
+ // Divide sizeLeft evenly among the struct fields.
+ sizeLeft := size
+ if n > sizeLeft {
+ sizeLeft = 1
+ } else if n > 0 {
+ sizeLeft /= n
+ }
+ for i := 0; i < n; i++ {
+ elem, ok := sizedValue(concrete.Field(i).Type, rand, sizeLeft)
+ if !ok {
+ return reflect.Value{}, false
+ }
+ v.Field(i).Set(elem)
+ }
+ default:
+ return reflect.Value{}, false
+ }
+
+ return v, true
+}
+
+// A Config structure contains options for running a test.
+type Config struct {
+ // MaxCount sets the maximum number of iterations.
+ // If zero, MaxCountScale is used.
+ MaxCount int
+ // MaxCountScale is a non-negative scale factor applied to the
+ // default maximum.
+ // A count of zero implies the default, which is usually 100
+ // but can be set by the -quickchecks flag.
+ MaxCountScale float64
+ // Rand specifies a source of random numbers.
+ // If nil, a default pseudo-random source will be used.
+ Rand *rand.Rand
+ // Values specifies a function to generate a slice of
+ // arbitrary reflect.Values that are congruent with the
+ // arguments to the function being tested.
+ // If nil, the top-level Value function is used to generate them.
+ Values func([]reflect.Value, *rand.Rand)
+}
+
+var defaultConfig Config
+
+// getRand returns the *rand.Rand to use for a given Config.
+func (c *Config) getRand() *rand.Rand {
+ if c.Rand == nil {
+ return rand.New(rand.NewSource(time.Now().UnixNano()))
+ }
+ return c.Rand
+}
+
+// getMaxCount returns the maximum number of iterations to run for a given
+// Config.
+func (c *Config) getMaxCount() (maxCount int) {
+ maxCount = c.MaxCount
+ if maxCount == 0 {
+ if c.MaxCountScale != 0 {
+ maxCount = int(c.MaxCountScale * float64(*defaultMaxCount))
+ } else {
+ maxCount = *defaultMaxCount
+ }
+ }
+
+ return
+}
+
+// A SetupError is the result of an error in the way that check is being
+// used, independent of the functions being tested.
+type SetupError string
+
+func (s SetupError) Error() string { return string(s) }
+
+// A CheckError is the result of Check finding an error.
+type CheckError struct {
+ Count int
+ In []any
+}
+
+func (s *CheckError) Error() string {
+ return fmt.Sprintf("#%d: failed on input %s", s.Count, toString(s.In))
+}
+
+// A CheckEqualError is the result [CheckEqual] finding an error.
+type CheckEqualError struct {
+ CheckError
+ Out1 []any
+ Out2 []any
+}
+
+func (s *CheckEqualError) Error() string {
+ return fmt.Sprintf("#%d: failed on input %s. Output 1: %s. Output 2: %s", s.Count, toString(s.In), toString(s.Out1), toString(s.Out2))
+}
+
+// Check looks for an input to f, any function that returns bool,
+// such that f returns false. It calls f repeatedly, with arbitrary
+// values for each argument. If f returns false on a given input,
+// Check returns that input as a *[CheckError].
+// For example:
+//
+// func TestOddMultipleOfThree(t *testing.T) {
+// f := func(x int) bool {
+// y := OddMultipleOfThree(x)
+// return y%2 == 1 && y%3 == 0
+// }
+// if err := quick.Check(f, nil); err != nil {
+// t.Error(err)
+// }
+// }
+func Check(f any, config *Config) error {
+ if config == nil {
+ config = &defaultConfig
+ }
+
+ fVal, fType, ok := functionAndType(f)
+ if !ok {
+ return SetupError("argument is not a function")
+ }
+
+ if fType.NumOut() != 1 {
+ return SetupError("function does not return one value")
+ }
+ if fType.Out(0).Kind() != reflect.Bool {
+ return SetupError("function does not return a bool")
+ }
+
+ arguments := make([]reflect.Value, fType.NumIn())
+ rand := config.getRand()
+ maxCount := config.getMaxCount()
+
+ for i := 0; i < maxCount; i++ {
+ err := arbitraryValues(arguments, fType, config, rand)
+ if err != nil {
+ return err
+ }
+
+ if !fVal.Call(arguments)[0].Bool() {
+ return &CheckError{i + 1, toInterfaces(arguments)}
+ }
+ }
+
+ return nil
+}
+
+// CheckEqual looks for an input on which f and g return different results.
+// It calls f and g repeatedly with arbitrary values for each argument.
+// If f and g return different answers, CheckEqual returns a *[CheckEqualError]
+// describing the input and the outputs.
+func CheckEqual(f, g any, config *Config) error {
+ if config == nil {
+ config = &defaultConfig
+ }
+
+ x, xType, ok := functionAndType(f)
+ if !ok {
+ return SetupError("f is not a function")
+ }
+ y, yType, ok := functionAndType(g)
+ if !ok {
+ return SetupError("g is not a function")
+ }
+
+ if xType != yType {
+ return SetupError("functions have different types")
+ }
+
+ arguments := make([]reflect.Value, xType.NumIn())
+ rand := config.getRand()
+ maxCount := config.getMaxCount()
+
+ for i := 0; i < maxCount; i++ {
+ err := arbitraryValues(arguments, xType, config, rand)
+ if err != nil {
+ return err
+ }
+
+ xOut := toInterfaces(x.Call(arguments))
+ yOut := toInterfaces(y.Call(arguments))
+
+ if !reflect.DeepEqual(xOut, yOut) {
+ return &CheckEqualError{CheckError{i + 1, toInterfaces(arguments)}, xOut, yOut}
+ }
+ }
+
+ return nil
+}
+
+// arbitraryValues writes Values to args such that args contains Values
+// suitable for calling f.
+func arbitraryValues(args []reflect.Value, f reflect.Type, config *Config, rand *rand.Rand) (err error) {
+ if config.Values != nil {
+ config.Values(args, rand)
+ return
+ }
+
+ for j := 0; j < len(args); j++ {
+ var ok bool
+ args[j], ok = Value(f.In(j), rand)
+ if !ok {
+ err = SetupError(fmt.Sprintf("cannot create arbitrary value of type %s for argument %d", f.In(j), j))
+ return
+ }
+ }
+
+ return
+}
+
+func functionAndType(f any) (v reflect.Value, t reflect.Type, ok bool) {
+ v = reflect.ValueOf(f)
+ ok = v.Kind() == reflect.Func
+ if !ok {
+ return
+ }
+ t = v.Type()
+ return
+}
+
+func toInterfaces(values []reflect.Value) []any {
+ ret := make([]any, len(values))
+ for i, v := range values {
+ ret[i] = v.Interface()
+ }
+ return ret
+}
+
+func toString(interfaces []any) string {
+ s := make([]string, len(interfaces))
+ for i, v := range interfaces {
+ s[i] = fmt.Sprintf("%#v", v)
+ }
+ return strings.Join(s, ", ")
+}
diff --git a/src/testing/quick/quick_test.go b/src/testing/quick/quick_test.go
new file mode 100644
index 0000000..9df6dd4
--- /dev/null
+++ b/src/testing/quick/quick_test.go
@@ -0,0 +1,327 @@
+// Copyright 2009 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 quick
+
+import (
+ "math/rand"
+ "reflect"
+ "testing"
+)
+
+func fArray(a [4]byte) [4]byte { return a }
+
+type TestArrayAlias [4]byte
+
+func fArrayAlias(a TestArrayAlias) TestArrayAlias { return a }
+
+func fBool(a bool) bool { return a }
+
+type TestBoolAlias bool
+
+func fBoolAlias(a TestBoolAlias) TestBoolAlias { return a }
+
+func fFloat32(a float32) float32 { return a }
+
+type TestFloat32Alias float32
+
+func fFloat32Alias(a TestFloat32Alias) TestFloat32Alias { return a }
+
+func fFloat64(a float64) float64 { return a }
+
+type TestFloat64Alias float64
+
+func fFloat64Alias(a TestFloat64Alias) TestFloat64Alias { return a }
+
+func fComplex64(a complex64) complex64 { return a }
+
+type TestComplex64Alias complex64
+
+func fComplex64Alias(a TestComplex64Alias) TestComplex64Alias { return a }
+
+func fComplex128(a complex128) complex128 { return a }
+
+type TestComplex128Alias complex128
+
+func fComplex128Alias(a TestComplex128Alias) TestComplex128Alias { return a }
+
+func fInt16(a int16) int16 { return a }
+
+type TestInt16Alias int16
+
+func fInt16Alias(a TestInt16Alias) TestInt16Alias { return a }
+
+func fInt32(a int32) int32 { return a }
+
+type TestInt32Alias int32
+
+func fInt32Alias(a TestInt32Alias) TestInt32Alias { return a }
+
+func fInt64(a int64) int64 { return a }
+
+type TestInt64Alias int64
+
+func fInt64Alias(a TestInt64Alias) TestInt64Alias { return a }
+
+func fInt8(a int8) int8 { return a }
+
+type TestInt8Alias int8
+
+func fInt8Alias(a TestInt8Alias) TestInt8Alias { return a }
+
+func fInt(a int) int { return a }
+
+type TestIntAlias int
+
+func fIntAlias(a TestIntAlias) TestIntAlias { return a }
+
+func fMap(a map[int]int) map[int]int { return a }
+
+type TestMapAlias map[int]int
+
+func fMapAlias(a TestMapAlias) TestMapAlias { return a }
+
+func fPtr(a *int) *int {
+ if a == nil {
+ return nil
+ }
+ b := *a
+ return &b
+}
+
+type TestPtrAlias *int
+
+func fPtrAlias(a TestPtrAlias) TestPtrAlias { return a }
+
+func fSlice(a []byte) []byte { return a }
+
+type TestSliceAlias []byte
+
+func fSliceAlias(a TestSliceAlias) TestSliceAlias { return a }
+
+func fString(a string) string { return a }
+
+type TestStringAlias string
+
+func fStringAlias(a TestStringAlias) TestStringAlias { return a }
+
+type TestStruct struct {
+ A int
+ B string
+}
+
+func fStruct(a TestStruct) TestStruct { return a }
+
+type TestStructAlias TestStruct
+
+func fStructAlias(a TestStructAlias) TestStructAlias { return a }
+
+func fUint16(a uint16) uint16 { return a }
+
+type TestUint16Alias uint16
+
+func fUint16Alias(a TestUint16Alias) TestUint16Alias { return a }
+
+func fUint32(a uint32) uint32 { return a }
+
+type TestUint32Alias uint32
+
+func fUint32Alias(a TestUint32Alias) TestUint32Alias { return a }
+
+func fUint64(a uint64) uint64 { return a }
+
+type TestUint64Alias uint64
+
+func fUint64Alias(a TestUint64Alias) TestUint64Alias { return a }
+
+func fUint8(a uint8) uint8 { return a }
+
+type TestUint8Alias uint8
+
+func fUint8Alias(a TestUint8Alias) TestUint8Alias { return a }
+
+func fUint(a uint) uint { return a }
+
+type TestUintAlias uint
+
+func fUintAlias(a TestUintAlias) TestUintAlias { return a }
+
+func fUintptr(a uintptr) uintptr { return a }
+
+type TestUintptrAlias uintptr
+
+func fUintptrAlias(a TestUintptrAlias) TestUintptrAlias { return a }
+
+func reportError(property string, err error, t *testing.T) {
+ if err != nil {
+ t.Errorf("%s: %s", property, err)
+ }
+}
+
+func TestCheckEqual(t *testing.T) {
+ reportError("fArray", CheckEqual(fArray, fArray, nil), t)
+ reportError("fArrayAlias", CheckEqual(fArrayAlias, fArrayAlias, nil), t)
+ reportError("fBool", CheckEqual(fBool, fBool, nil), t)
+ reportError("fBoolAlias", CheckEqual(fBoolAlias, fBoolAlias, nil), t)
+ reportError("fFloat32", CheckEqual(fFloat32, fFloat32, nil), t)
+ reportError("fFloat32Alias", CheckEqual(fFloat32Alias, fFloat32Alias, nil), t)
+ reportError("fFloat64", CheckEqual(fFloat64, fFloat64, nil), t)
+ reportError("fFloat64Alias", CheckEqual(fFloat64Alias, fFloat64Alias, nil), t)
+ reportError("fComplex64", CheckEqual(fComplex64, fComplex64, nil), t)
+ reportError("fComplex64Alias", CheckEqual(fComplex64Alias, fComplex64Alias, nil), t)
+ reportError("fComplex128", CheckEqual(fComplex128, fComplex128, nil), t)
+ reportError("fComplex128Alias", CheckEqual(fComplex128Alias, fComplex128Alias, nil), t)
+ reportError("fInt16", CheckEqual(fInt16, fInt16, nil), t)
+ reportError("fInt16Alias", CheckEqual(fInt16Alias, fInt16Alias, nil), t)
+ reportError("fInt32", CheckEqual(fInt32, fInt32, nil), t)
+ reportError("fInt32Alias", CheckEqual(fInt32Alias, fInt32Alias, nil), t)
+ reportError("fInt64", CheckEqual(fInt64, fInt64, nil), t)
+ reportError("fInt64Alias", CheckEqual(fInt64Alias, fInt64Alias, nil), t)
+ reportError("fInt8", CheckEqual(fInt8, fInt8, nil), t)
+ reportError("fInt8Alias", CheckEqual(fInt8Alias, fInt8Alias, nil), t)
+ reportError("fInt", CheckEqual(fInt, fInt, nil), t)
+ reportError("fIntAlias", CheckEqual(fIntAlias, fIntAlias, nil), t)
+ reportError("fInt32", CheckEqual(fInt32, fInt32, nil), t)
+ reportError("fInt32Alias", CheckEqual(fInt32Alias, fInt32Alias, nil), t)
+ reportError("fMap", CheckEqual(fMap, fMap, nil), t)
+ reportError("fMapAlias", CheckEqual(fMapAlias, fMapAlias, nil), t)
+ reportError("fPtr", CheckEqual(fPtr, fPtr, nil), t)
+ reportError("fPtrAlias", CheckEqual(fPtrAlias, fPtrAlias, nil), t)
+ reportError("fSlice", CheckEqual(fSlice, fSlice, nil), t)
+ reportError("fSliceAlias", CheckEqual(fSliceAlias, fSliceAlias, nil), t)
+ reportError("fString", CheckEqual(fString, fString, nil), t)
+ reportError("fStringAlias", CheckEqual(fStringAlias, fStringAlias, nil), t)
+ reportError("fStruct", CheckEqual(fStruct, fStruct, nil), t)
+ reportError("fStructAlias", CheckEqual(fStructAlias, fStructAlias, nil), t)
+ reportError("fUint16", CheckEqual(fUint16, fUint16, nil), t)
+ reportError("fUint16Alias", CheckEqual(fUint16Alias, fUint16Alias, nil), t)
+ reportError("fUint32", CheckEqual(fUint32, fUint32, nil), t)
+ reportError("fUint32Alias", CheckEqual(fUint32Alias, fUint32Alias, nil), t)
+ reportError("fUint64", CheckEqual(fUint64, fUint64, nil), t)
+ reportError("fUint64Alias", CheckEqual(fUint64Alias, fUint64Alias, nil), t)
+ reportError("fUint8", CheckEqual(fUint8, fUint8, nil), t)
+ reportError("fUint8Alias", CheckEqual(fUint8Alias, fUint8Alias, nil), t)
+ reportError("fUint", CheckEqual(fUint, fUint, nil), t)
+ reportError("fUintAlias", CheckEqual(fUintAlias, fUintAlias, nil), t)
+ reportError("fUintptr", CheckEqual(fUintptr, fUintptr, nil), t)
+ reportError("fUintptrAlias", CheckEqual(fUintptrAlias, fUintptrAlias, nil), t)
+}
+
+// This tests that ArbitraryValue is working by checking that all the arbitrary
+// values of type MyStruct have x = 42.
+type myStruct struct {
+ x int
+}
+
+func (m myStruct) Generate(r *rand.Rand, _ int) reflect.Value {
+ return reflect.ValueOf(myStruct{x: 42})
+}
+
+func myStructProperty(in myStruct) bool { return in.x == 42 }
+
+func TestCheckProperty(t *testing.T) {
+ reportError("myStructProperty", Check(myStructProperty, nil), t)
+}
+
+func TestFailure(t *testing.T) {
+ f := func(x int) bool { return false }
+ err := Check(f, nil)
+ if err == nil {
+ t.Errorf("Check didn't return an error")
+ }
+ if _, ok := err.(*CheckError); !ok {
+ t.Errorf("Error was not a CheckError: %s", err)
+ }
+
+ err = CheckEqual(fUint, fUint32, nil)
+ if err == nil {
+ t.Errorf("#1 CheckEqual didn't return an error")
+ }
+ if _, ok := err.(SetupError); !ok {
+ t.Errorf("#1 Error was not a SetupError: %s", err)
+ }
+
+ err = CheckEqual(func(x, y int) {}, func(x int) {}, nil)
+ if err == nil {
+ t.Errorf("#2 CheckEqual didn't return an error")
+ }
+ if _, ok := err.(SetupError); !ok {
+ t.Errorf("#2 Error was not a SetupError: %s", err)
+ }
+
+ err = CheckEqual(func(x int) int { return 0 }, func(x int) int32 { return 0 }, nil)
+ if err == nil {
+ t.Errorf("#3 CheckEqual didn't return an error")
+ }
+ if _, ok := err.(SetupError); !ok {
+ t.Errorf("#3 Error was not a SetupError: %s", err)
+ }
+}
+
+// Recursive data structures didn't terminate.
+// Issues 8818 and 11148.
+func TestRecursive(t *testing.T) {
+ type R struct {
+ Ptr *R
+ SliceP []*R
+ Slice []R
+ Map map[int]R
+ MapP map[int]*R
+ MapR map[*R]*R
+ SliceMap []map[int]R
+ }
+
+ f := func(r R) bool { return true }
+ Check(f, nil)
+}
+
+func TestEmptyStruct(t *testing.T) {
+ f := func(struct{}) bool { return true }
+ Check(f, nil)
+}
+
+type (
+ A struct{ B *B }
+ B struct{ A *A }
+)
+
+func TestMutuallyRecursive(t *testing.T) {
+ f := func(a A) bool { return true }
+ Check(f, nil)
+}
+
+// Some serialization formats (e.g. encoding/pem) cannot distinguish
+// between a nil and an empty map or slice, so avoid generating the
+// zero value for these.
+func TestNonZeroSliceAndMap(t *testing.T) {
+ type Q struct {
+ M map[int]int
+ S []int
+ }
+ f := func(q Q) bool {
+ return q.M != nil && q.S != nil
+ }
+ err := Check(f, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestInt64(t *testing.T) {
+ var lo, hi int64
+ f := func(x int64) bool {
+ if x < lo {
+ lo = x
+ }
+ if x > hi {
+ hi = x
+ }
+ return true
+ }
+ cfg := &Config{MaxCount: 10000}
+ Check(f, cfg)
+ if uint64(lo)>>62 == 0 || uint64(hi)>>62 == 0 {
+ t.Errorf("int64 returned range %#016x,%#016x; does not look like full range", lo, hi)
+ }
+}
diff --git a/src/testing/run_example.go b/src/testing/run_example.go
new file mode 100644
index 0000000..b2c5c3d
--- /dev/null
+++ b/src/testing/run_example.go
@@ -0,0 +1,66 @@
+// Copyright 2019 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.
+
+//go:build !js && !wasip1
+
+// TODO(@musiol, @odeke-em): re-unify this entire file back into
+// example.go when js/wasm gets an os.Pipe implementation
+// and no longer needs this separation.
+
+package testing
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "strings"
+ "time"
+)
+
+func runExample(eg InternalExample) (ok bool) {
+ if chatty.on {
+ fmt.Printf("%s=== RUN %s\n", chatty.prefix(), eg.Name)
+ }
+
+ // Capture stdout.
+ stdout := os.Stdout
+ r, w, err := os.Pipe()
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ os.Stdout = w
+ outC := make(chan string)
+ go func() {
+ var buf strings.Builder
+ _, err := io.Copy(&buf, r)
+ r.Close()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "testing: copying pipe: %v\n", err)
+ os.Exit(1)
+ }
+ outC <- buf.String()
+ }()
+
+ finished := false
+ start := time.Now()
+
+ // Clean up in a deferred call so we can recover if the example panics.
+ defer func() {
+ timeSpent := time.Since(start)
+
+ // Close pipe, restore stdout, get output.
+ w.Close()
+ os.Stdout = stdout
+ out := <-outC
+
+ err := recover()
+ ok = eg.processRunResult(out, timeSpent, finished, err)
+ }()
+
+ // Run example.
+ eg.F()
+ finished = true
+ return
+}
diff --git a/src/testing/run_example_wasm.go b/src/testing/run_example_wasm.go
new file mode 100644
index 0000000..b815fcd
--- /dev/null
+++ b/src/testing/run_example_wasm.go
@@ -0,0 +1,76 @@
+// Copyright 2019 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.
+
+//go:build js || wasip1
+
+package testing
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "strings"
+ "time"
+)
+
+// TODO(@musiol, @odeke-em): unify this code back into
+// example.go when js/wasm gets an os.Pipe implementation.
+func runExample(eg InternalExample) (ok bool) {
+ if chatty.on {
+ fmt.Printf("%s=== RUN %s\n", chatty.prefix(), eg.Name)
+ }
+
+ // Capture stdout to temporary file. We're not using
+ // os.Pipe because it is not supported on js/wasm.
+ stdout := os.Stdout
+ f := createTempFile(eg.Name)
+ os.Stdout = f
+ finished := false
+ start := time.Now()
+
+ // Clean up in a deferred call so we can recover if the example panics.
+ defer func() {
+ timeSpent := time.Since(start)
+
+ // Restore stdout, get output and remove temporary file.
+ os.Stdout = stdout
+ var buf strings.Builder
+ _, seekErr := f.Seek(0, io.SeekStart)
+ _, readErr := io.Copy(&buf, f)
+ out := buf.String()
+ f.Close()
+ os.Remove(f.Name())
+ if seekErr != nil {
+ fmt.Fprintf(os.Stderr, "testing: seek temp file: %v\n", seekErr)
+ os.Exit(1)
+ }
+ if readErr != nil {
+ fmt.Fprintf(os.Stderr, "testing: read temp file: %v\n", readErr)
+ os.Exit(1)
+ }
+
+ err := recover()
+ ok = eg.processRunResult(out, timeSpent, finished, err)
+ }()
+
+ // Run example.
+ eg.F()
+ finished = true
+ return
+}
+
+func createTempFile(exampleName string) *os.File {
+ for i := 0; ; i++ {
+ name := fmt.Sprintf("%s/go-example-stdout-%s-%d.txt", os.TempDir(), exampleName, i)
+ f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
+ if err != nil {
+ if os.IsExist(err) {
+ continue
+ }
+ fmt.Fprintf(os.Stderr, "testing: open temp file: %v\n", err)
+ os.Exit(1)
+ }
+ return f
+ }
+}
diff --git a/src/testing/slogtest/example_test.go b/src/testing/slogtest/example_test.go
new file mode 100644
index 0000000..0517a4b
--- /dev/null
+++ b/src/testing/slogtest/example_test.go
@@ -0,0 +1,44 @@
+// Copyright 2023 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 slogtest_test
+
+import (
+ "bytes"
+ "encoding/json"
+ "log"
+ "log/slog"
+ "testing/slogtest"
+)
+
+// This example demonstrates one technique for testing a handler with this
+// package. The handler is given a [bytes.Buffer] to write to, and each line
+// of the resulting output is parsed.
+// For JSON output, [encoding/json.Unmarshal] produces a result in the desired
+// format when given a pointer to a map[string]any.
+func Example_parsing() {
+ var buf bytes.Buffer
+ h := slog.NewJSONHandler(&buf, nil)
+
+ results := func() []map[string]any {
+ var ms []map[string]any
+ for _, line := range bytes.Split(buf.Bytes(), []byte{'\n'}) {
+ if len(line) == 0 {
+ continue
+ }
+ var m map[string]any
+ if err := json.Unmarshal(line, &m); err != nil {
+ panic(err) // In a real test, use t.Fatal.
+ }
+ ms = append(ms, m)
+ }
+ return ms
+ }
+ err := slogtest.TestHandler(h, results)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Output:
+}
diff --git a/src/testing/slogtest/run_test.go b/src/testing/slogtest/run_test.go
new file mode 100644
index 0000000..c82da10
--- /dev/null
+++ b/src/testing/slogtest/run_test.go
@@ -0,0 +1,31 @@
+// Copyright 2023 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 slogtest_test
+
+import (
+ "bytes"
+ "encoding/json"
+ "log/slog"
+ "testing"
+ "testing/slogtest"
+)
+
+func TestRun(t *testing.T) {
+ var buf bytes.Buffer
+
+ newHandler := func(*testing.T) slog.Handler {
+ buf.Reset()
+ return slog.NewJSONHandler(&buf, nil)
+ }
+ result := func(t *testing.T) map[string]any {
+ m := map[string]any{}
+ if err := json.Unmarshal(buf.Bytes(), &m); err != nil {
+ t.Fatal(err)
+ }
+ return m
+ }
+
+ slogtest.Run(t, newHandler, result)
+}
diff --git a/src/testing/slogtest/slogtest.go b/src/testing/slogtest/slogtest.go
new file mode 100644
index 0000000..5c3aced
--- /dev/null
+++ b/src/testing/slogtest/slogtest.go
@@ -0,0 +1,375 @@
+// Copyright 2023 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 slogtest implements support for testing implementations of log/slog.Handler.
+package slogtest
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "log/slog"
+ "reflect"
+ "runtime"
+ "testing"
+ "time"
+)
+
+type testCase struct {
+ // Subtest name.
+ name string
+ // If non-empty, explanation explains the violated constraint.
+ explanation string
+ // f executes a single log event using its argument logger.
+ // So that mkdescs.sh can generate the right description,
+ // the body of f must appear on a single line whose first
+ // non-whitespace characters are "l.".
+ f func(*slog.Logger)
+ // If mod is not nil, it is called to modify the Record
+ // generated by the Logger before it is passed to the Handler.
+ mod func(*slog.Record)
+ // checks is a list of checks to run on the result.
+ checks []check
+}
+
+var cases = []testCase{
+ {
+ name: "built-ins",
+ explanation: withSource("this test expects slog.TimeKey, slog.LevelKey and slog.MessageKey"),
+ f: func(l *slog.Logger) {
+ l.Info("message")
+ },
+ checks: []check{
+ hasKey(slog.TimeKey),
+ hasKey(slog.LevelKey),
+ hasAttr(slog.MessageKey, "message"),
+ },
+ },
+ {
+ name: "attrs",
+ explanation: withSource("a Handler should output attributes passed to the logging function"),
+ f: func(l *slog.Logger) {
+ l.Info("message", "k", "v")
+ },
+ checks: []check{
+ hasAttr("k", "v"),
+ },
+ },
+ {
+ name: "empty-attr",
+ explanation: withSource("a Handler should ignore an empty Attr"),
+ f: func(l *slog.Logger) {
+ l.Info("msg", "a", "b", "", nil, "c", "d")
+ },
+ checks: []check{
+ hasAttr("a", "b"),
+ missingKey(""),
+ hasAttr("c", "d"),
+ },
+ },
+ {
+ name: "zero-time",
+ explanation: withSource("a Handler should ignore a zero Record.Time"),
+ f: func(l *slog.Logger) {
+ l.Info("msg", "k", "v")
+ },
+ mod: func(r *slog.Record) { r.Time = time.Time{} },
+ checks: []check{
+ missingKey(slog.TimeKey),
+ },
+ },
+ {
+ name: "WithAttrs",
+ explanation: withSource("a Handler should include the attributes from the WithAttrs method"),
+ f: func(l *slog.Logger) {
+ l.With("a", "b").Info("msg", "k", "v")
+ },
+ checks: []check{
+ hasAttr("a", "b"),
+ hasAttr("k", "v"),
+ },
+ },
+ {
+ name: "groups",
+ explanation: withSource("a Handler should handle Group attributes"),
+ f: func(l *slog.Logger) {
+ l.Info("msg", "a", "b", slog.Group("G", slog.String("c", "d")), "e", "f")
+ },
+ checks: []check{
+ hasAttr("a", "b"),
+ inGroup("G", hasAttr("c", "d")),
+ hasAttr("e", "f"),
+ },
+ },
+ {
+ name: "empty-group",
+ explanation: withSource("a Handler should ignore an empty group"),
+ f: func(l *slog.Logger) {
+ l.Info("msg", "a", "b", slog.Group("G"), "e", "f")
+ },
+ checks: []check{
+ hasAttr("a", "b"),
+ missingKey("G"),
+ hasAttr("e", "f"),
+ },
+ },
+ {
+ name: "inline-group",
+ explanation: withSource("a Handler should inline the Attrs of a group with an empty key"),
+ f: func(l *slog.Logger) {
+ l.Info("msg", "a", "b", slog.Group("", slog.String("c", "d")), "e", "f")
+
+ },
+ checks: []check{
+ hasAttr("a", "b"),
+ hasAttr("c", "d"),
+ hasAttr("e", "f"),
+ },
+ },
+ {
+ name: "WithGroup",
+ explanation: withSource("a Handler should handle the WithGroup method"),
+ f: func(l *slog.Logger) {
+ l.WithGroup("G").Info("msg", "a", "b")
+ },
+ checks: []check{
+ hasKey(slog.TimeKey),
+ hasKey(slog.LevelKey),
+ hasAttr(slog.MessageKey, "msg"),
+ missingKey("a"),
+ inGroup("G", hasAttr("a", "b")),
+ },
+ },
+ {
+ name: "multi-With",
+ explanation: withSource("a Handler should handle multiple WithGroup and WithAttr calls"),
+ f: func(l *slog.Logger) {
+ l.With("a", "b").WithGroup("G").With("c", "d").WithGroup("H").Info("msg", "e", "f")
+ },
+ checks: []check{
+ hasKey(slog.TimeKey),
+ hasKey(slog.LevelKey),
+ hasAttr(slog.MessageKey, "msg"),
+ hasAttr("a", "b"),
+ inGroup("G", hasAttr("c", "d")),
+ inGroup("G", inGroup("H", hasAttr("e", "f"))),
+ },
+ },
+ {
+ name: "empty-group-record",
+ explanation: withSource("a Handler should not output groups if there are no attributes"),
+ f: func(l *slog.Logger) {
+ l.With("a", "b").WithGroup("G").With("c", "d").WithGroup("H").Info("msg")
+ },
+ checks: []check{
+ hasKey(slog.TimeKey),
+ hasKey(slog.LevelKey),
+ hasAttr(slog.MessageKey, "msg"),
+ hasAttr("a", "b"),
+ inGroup("G", hasAttr("c", "d")),
+ inGroup("G", missingKey("H")),
+ },
+ },
+ {
+ name: "resolve",
+ explanation: withSource("a Handler should call Resolve on attribute values"),
+ f: func(l *slog.Logger) {
+ l.Info("msg", "k", &replace{"replaced"})
+ },
+ checks: []check{hasAttr("k", "replaced")},
+ },
+ {
+ name: "resolve-groups",
+ explanation: withSource("a Handler should call Resolve on attribute values in groups"),
+ f: func(l *slog.Logger) {
+ l.Info("msg",
+ slog.Group("G",
+ slog.String("a", "v1"),
+ slog.Any("b", &replace{"v2"})))
+ },
+ checks: []check{
+ inGroup("G", hasAttr("a", "v1")),
+ inGroup("G", hasAttr("b", "v2")),
+ },
+ },
+ {
+ name: "resolve-WithAttrs",
+ explanation: withSource("a Handler should call Resolve on attribute values from WithAttrs"),
+ f: func(l *slog.Logger) {
+ l = l.With("k", &replace{"replaced"})
+ l.Info("msg")
+ },
+ checks: []check{hasAttr("k", "replaced")},
+ },
+ {
+ name: "resolve-WithAttrs-groups",
+ explanation: withSource("a Handler should call Resolve on attribute values in groups from WithAttrs"),
+ f: func(l *slog.Logger) {
+ l = l.With(slog.Group("G",
+ slog.String("a", "v1"),
+ slog.Any("b", &replace{"v2"})))
+ l.Info("msg")
+ },
+ checks: []check{
+ inGroup("G", hasAttr("a", "v1")),
+ inGroup("G", hasAttr("b", "v2")),
+ },
+ },
+ {
+ name: "empty-PC",
+ explanation: withSource("a Handler should not output SourceKey if the PC is zero"),
+ f: func(l *slog.Logger) {
+ l.Info("message")
+ },
+ mod: func(r *slog.Record) { r.PC = 0 },
+ checks: []check{
+ missingKey(slog.SourceKey),
+ },
+ },
+}
+
+// TestHandler tests a [slog.Handler].
+// If TestHandler finds any misbehaviors, it returns an error for each,
+// combined into a single error with [errors.Join].
+//
+// TestHandler installs the given Handler in a [slog.Logger] and
+// makes several calls to the Logger's output methods.
+// The Handler should be enabled for levels Info and above.
+//
+// The results function is invoked after all such calls.
+// It should return a slice of map[string]any, one for each call to a Logger output method.
+// The keys and values of the map should correspond to the keys and values of the Handler's
+// output. Each group in the output should be represented as its own nested map[string]any.
+// The standard keys [slog.TimeKey], [slog.LevelKey] and [slog.MessageKey] should be used.
+//
+// If the Handler outputs JSON, then calling [encoding/json.Unmarshal] with a `map[string]any`
+// will create the right data structure.
+//
+// If a Handler intentionally drops an attribute that is checked by a test,
+// then the results function should check for its absence and add it to the map it returns.
+func TestHandler(h slog.Handler, results func() []map[string]any) error {
+ // Run the handler on the test cases.
+ for _, c := range cases {
+ ht := h
+ if c.mod != nil {
+ ht = &wrapper{h, c.mod}
+ }
+ l := slog.New(ht)
+ c.f(l)
+ }
+
+ // Collect and check the results.
+ var errs []error
+ res := results()
+ if g, w := len(res), len(cases); g != w {
+ return fmt.Errorf("got %d results, want %d", g, w)
+ }
+ for i, got := range results() {
+ c := cases[i]
+ for _, check := range c.checks {
+ if problem := check(got); problem != "" {
+ errs = append(errs, fmt.Errorf("%s: %s", problem, c.explanation))
+ }
+ }
+ }
+ return errors.Join(errs...)
+}
+
+// Run exercises a [slog.Handler] on the same test cases as [TestHandler], but
+// runs each case in a subtest. For each test case, it first calls newHandler to
+// get an instance of the handler under test, then runs the test case, then
+// calls result to get the result. If the test case fails, it calls t.Error.
+func Run(t *testing.T, newHandler func(*testing.T) slog.Handler, result func(*testing.T) map[string]any) {
+ for _, c := range cases {
+ t.Run(c.name, func(t *testing.T) {
+ h := newHandler(t)
+ if c.mod != nil {
+ h = &wrapper{h, c.mod}
+ }
+ l := slog.New(h)
+ c.f(l)
+ got := result(t)
+ for _, check := range c.checks {
+ if p := check(got); p != "" {
+ t.Errorf("%s: %s", p, c.explanation)
+ }
+ }
+ })
+ }
+}
+
+type check func(map[string]any) string
+
+func hasKey(key string) check {
+ return func(m map[string]any) string {
+ if _, ok := m[key]; !ok {
+ return fmt.Sprintf("missing key %q", key)
+ }
+ return ""
+ }
+}
+
+func missingKey(key string) check {
+ return func(m map[string]any) string {
+ if _, ok := m[key]; ok {
+ return fmt.Sprintf("unexpected key %q", key)
+ }
+ return ""
+ }
+}
+
+func hasAttr(key string, wantVal any) check {
+ return func(m map[string]any) string {
+ if s := hasKey(key)(m); s != "" {
+ return s
+ }
+ gotVal := m[key]
+ if !reflect.DeepEqual(gotVal, wantVal) {
+ return fmt.Sprintf("%q: got %#v, want %#v", key, gotVal, wantVal)
+ }
+ return ""
+ }
+}
+
+func inGroup(name string, c check) check {
+ return func(m map[string]any) string {
+ v, ok := m[name]
+ if !ok {
+ return fmt.Sprintf("missing group %q", name)
+ }
+ g, ok := v.(map[string]any)
+ if !ok {
+ return fmt.Sprintf("value for group %q is not map[string]any", name)
+ }
+ return c(g)
+ }
+}
+
+type wrapper struct {
+ slog.Handler
+ mod func(*slog.Record)
+}
+
+func (h *wrapper) Handle(ctx context.Context, r slog.Record) error {
+ h.mod(&r)
+ return h.Handler.Handle(ctx, r)
+}
+
+func withSource(s string) string {
+ _, file, line, ok := runtime.Caller(1)
+ if !ok {
+ panic("runtime.Caller failed")
+ }
+ return fmt.Sprintf("%s (%s:%d)", s, file, line)
+}
+
+type replace struct {
+ v any
+}
+
+func (r *replace) LogValue() slog.Value { return slog.AnyValue(r.v) }
+
+func (r *replace) String() string {
+ return fmt.Sprintf("<replace(%v)>", r.v)
+}
diff --git a/src/testing/sub_test.go b/src/testing/sub_test.go
new file mode 100644
index 0000000..1c23d05
--- /dev/null
+++ b/src/testing/sub_test.go
@@ -0,0 +1,992 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testing
+
+import (
+ "bytes"
+ "fmt"
+ "reflect"
+ "regexp"
+ "runtime"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "time"
+)
+
+func init() {
+ // Make benchmark tests run 10x faster.
+ benchTime.d = 100 * time.Millisecond
+}
+
+func TestTestContext(t *T) {
+ const (
+ add1 = 0
+ done = 1
+ )
+ // After each of the calls are applied to the context, the
+ type call struct {
+ typ int // run or done
+ // result from applying the call
+ running int
+ waiting int
+ started bool
+ }
+ testCases := []struct {
+ max int
+ run []call
+ }{{
+ max: 1,
+ run: []call{
+ {typ: add1, running: 1, waiting: 0, started: true},
+ {typ: done, running: 0, waiting: 0, started: false},
+ },
+ }, {
+ max: 1,
+ run: []call{
+ {typ: add1, running: 1, waiting: 0, started: true},
+ {typ: add1, running: 1, waiting: 1, started: false},
+ {typ: done, running: 1, waiting: 0, started: true},
+ {typ: done, running: 0, waiting: 0, started: false},
+ {typ: add1, running: 1, waiting: 0, started: true},
+ },
+ }, {
+ max: 3,
+ run: []call{
+ {typ: add1, running: 1, waiting: 0, started: true},
+ {typ: add1, running: 2, waiting: 0, started: true},
+ {typ: add1, running: 3, waiting: 0, started: true},
+ {typ: add1, running: 3, waiting: 1, started: false},
+ {typ: add1, running: 3, waiting: 2, started: false},
+ {typ: add1, running: 3, waiting: 3, started: false},
+ {typ: done, running: 3, waiting: 2, started: true},
+ {typ: add1, running: 3, waiting: 3, started: false},
+ {typ: done, running: 3, waiting: 2, started: true},
+ {typ: done, running: 3, waiting: 1, started: true},
+ {typ: done, running: 3, waiting: 0, started: true},
+ {typ: done, running: 2, waiting: 0, started: false},
+ {typ: done, running: 1, waiting: 0, started: false},
+ {typ: done, running: 0, waiting: 0, started: false},
+ },
+ }}
+ for i, tc := range testCases {
+ ctx := &testContext{
+ startParallel: make(chan bool),
+ maxParallel: tc.max,
+ }
+ for j, call := range tc.run {
+ doCall := func(f func()) chan bool {
+ done := make(chan bool)
+ go func() {
+ f()
+ done <- true
+ }()
+ return done
+ }
+ started := false
+ switch call.typ {
+ case add1:
+ signal := doCall(ctx.waitParallel)
+ select {
+ case <-signal:
+ started = true
+ case ctx.startParallel <- true:
+ <-signal
+ }
+ case done:
+ signal := doCall(ctx.release)
+ select {
+ case <-signal:
+ case <-ctx.startParallel:
+ started = true
+ <-signal
+ }
+ }
+ if started != call.started {
+ t.Errorf("%d:%d:started: got %v; want %v", i, j, started, call.started)
+ }
+ if ctx.running != call.running {
+ t.Errorf("%d:%d:running: got %v; want %v", i, j, ctx.running, call.running)
+ }
+ if ctx.numWaiting != call.waiting {
+ t.Errorf("%d:%d:waiting: got %v; want %v", i, j, ctx.numWaiting, call.waiting)
+ }
+ }
+ }
+}
+
+func TestTRun(t *T) {
+ realTest := t
+ testCases := []struct {
+ desc string
+ ok bool
+ maxPar int
+ chatty bool
+ json bool
+ output string
+ f func(*T)
+ }{{
+ desc: "failnow skips future sequential and parallel tests at same level",
+ ok: false,
+ maxPar: 1,
+ output: `
+--- FAIL: failnow skips future sequential and parallel tests at same level (N.NNs)
+ --- FAIL: failnow skips future sequential and parallel tests at same level/#00 (N.NNs)
+ `,
+ f: func(t *T) {
+ ranSeq := false
+ ranPar := false
+ t.Run("", func(t *T) {
+ t.Run("par", func(t *T) {
+ t.Parallel()
+ ranPar = true
+ })
+ t.Run("seq", func(t *T) {
+ ranSeq = true
+ })
+ t.FailNow()
+ t.Run("seq", func(t *T) {
+ realTest.Error("test must be skipped")
+ })
+ t.Run("par", func(t *T) {
+ t.Parallel()
+ realTest.Error("test must be skipped.")
+ })
+ })
+ if !ranPar {
+ realTest.Error("parallel test was not run")
+ }
+ if !ranSeq {
+ realTest.Error("sequential test was not run")
+ }
+ },
+ }, {
+ desc: "failure in parallel test propagates upwards",
+ ok: false,
+ maxPar: 1,
+ output: `
+--- FAIL: failure in parallel test propagates upwards (N.NNs)
+ --- FAIL: failure in parallel test propagates upwards/#00 (N.NNs)
+ --- FAIL: failure in parallel test propagates upwards/#00/par (N.NNs)
+ `,
+ f: func(t *T) {
+ t.Run("", func(t *T) {
+ t.Parallel()
+ t.Run("par", func(t *T) {
+ t.Parallel()
+ t.Fail()
+ })
+ })
+ },
+ }, {
+ desc: "skipping without message, chatty",
+ ok: true,
+ chatty: true,
+ output: `
+=== RUN skipping without message, chatty
+--- SKIP: skipping without message, chatty (N.NNs)`,
+ f: func(t *T) { t.SkipNow() },
+ }, {
+ desc: "chatty with recursion",
+ ok: true,
+ chatty: true,
+ output: `
+=== RUN chatty with recursion
+=== RUN chatty with recursion/#00
+=== RUN chatty with recursion/#00/#00
+--- PASS: chatty with recursion (N.NNs)
+ --- PASS: chatty with recursion/#00 (N.NNs)
+ --- PASS: chatty with recursion/#00/#00 (N.NNs)`,
+ f: func(t *T) {
+ t.Run("", func(t *T) {
+ t.Run("", func(t *T) {})
+ })
+ },
+ }, {
+ desc: "chatty with recursion and json",
+ ok: false,
+ chatty: true,
+ json: true,
+ output: `
+^V=== RUN chatty with recursion and json
+^V=== RUN chatty with recursion and json/#00
+^V=== RUN chatty with recursion and json/#00/#00
+^V--- PASS: chatty with recursion and json/#00/#00 (N.NNs)
+^V=== NAME chatty with recursion and json/#00
+^V=== RUN chatty with recursion and json/#00/#01
+ sub_test.go:NNN: skip
+^V--- SKIP: chatty with recursion and json/#00/#01 (N.NNs)
+^V=== NAME chatty with recursion and json/#00
+^V=== RUN chatty with recursion and json/#00/#02
+ sub_test.go:NNN: fail
+^V--- FAIL: chatty with recursion and json/#00/#02 (N.NNs)
+^V=== NAME chatty with recursion and json/#00
+^V--- FAIL: chatty with recursion and json/#00 (N.NNs)
+^V=== NAME chatty with recursion and json
+^V--- FAIL: chatty with recursion and json (N.NNs)
+^V=== NAME `,
+ f: func(t *T) {
+ t.Run("", func(t *T) {
+ t.Run("", func(t *T) {})
+ t.Run("", func(t *T) { t.Skip("skip") })
+ t.Run("", func(t *T) { t.Fatal("fail") })
+ })
+ },
+ }, {
+ desc: "skipping without message, not chatty",
+ ok: true,
+ f: func(t *T) { t.SkipNow() },
+ }, {
+ desc: "skipping after error",
+ output: `
+--- FAIL: skipping after error (N.NNs)
+ sub_test.go:NNN: an error
+ sub_test.go:NNN: skipped`,
+ f: func(t *T) {
+ t.Error("an error")
+ t.Skip("skipped")
+ },
+ }, {
+ desc: "use Run to locally synchronize parallelism",
+ ok: true,
+ maxPar: 1,
+ f: func(t *T) {
+ var count uint32
+ t.Run("waitGroup", func(t *T) {
+ for i := 0; i < 4; i++ {
+ t.Run("par", func(t *T) {
+ t.Parallel()
+ atomic.AddUint32(&count, 1)
+ })
+ }
+ })
+ if count != 4 {
+ t.Errorf("count was %d; want 4", count)
+ }
+ },
+ }, {
+ desc: "alternate sequential and parallel",
+ // Sequential tests should partake in the counting of running threads.
+ // Otherwise, if one runs parallel subtests in sequential tests that are
+ // itself subtests of parallel tests, the counts can get askew.
+ ok: true,
+ maxPar: 1,
+ f: func(t *T) {
+ t.Run("a", func(t *T) {
+ t.Parallel()
+ t.Run("b", func(t *T) {
+ // Sequential: ensure running count is decremented.
+ t.Run("c", func(t *T) {
+ t.Parallel()
+ })
+
+ })
+ })
+ },
+ }, {
+ desc: "alternate sequential and parallel 2",
+ // Sequential tests should partake in the counting of running threads.
+ // Otherwise, if one runs parallel subtests in sequential tests that are
+ // itself subtests of parallel tests, the counts can get askew.
+ ok: true,
+ maxPar: 2,
+ f: func(t *T) {
+ for i := 0; i < 2; i++ {
+ t.Run("a", func(t *T) {
+ t.Parallel()
+ time.Sleep(time.Nanosecond)
+ for i := 0; i < 2; i++ {
+ t.Run("b", func(t *T) {
+ time.Sleep(time.Nanosecond)
+ for i := 0; i < 2; i++ {
+ t.Run("c", func(t *T) {
+ t.Parallel()
+ time.Sleep(time.Nanosecond)
+ })
+ }
+
+ })
+ }
+ })
+ }
+ },
+ }, {
+ desc: "stress test",
+ ok: true,
+ maxPar: 4,
+ f: func(t *T) {
+ t.Parallel()
+ for i := 0; i < 12; i++ {
+ t.Run("a", func(t *T) {
+ t.Parallel()
+ time.Sleep(time.Nanosecond)
+ for i := 0; i < 12; i++ {
+ t.Run("b", func(t *T) {
+ time.Sleep(time.Nanosecond)
+ for i := 0; i < 12; i++ {
+ t.Run("c", func(t *T) {
+ t.Parallel()
+ time.Sleep(time.Nanosecond)
+ t.Run("d1", func(t *T) {})
+ t.Run("d2", func(t *T) {})
+ t.Run("d3", func(t *T) {})
+ t.Run("d4", func(t *T) {})
+ })
+ }
+ })
+ }
+ })
+ }
+ },
+ }, {
+ desc: "skip output",
+ ok: true,
+ maxPar: 4,
+ f: func(t *T) {
+ t.Skip()
+ },
+ }, {
+ desc: "subtest calls error on parent",
+ ok: false,
+ output: `
+--- FAIL: subtest calls error on parent (N.NNs)
+ sub_test.go:NNN: first this
+ sub_test.go:NNN: and now this!
+ sub_test.go:NNN: oh, and this too`,
+ maxPar: 1,
+ f: func(t *T) {
+ t.Errorf("first this")
+ outer := t
+ t.Run("", func(t *T) {
+ outer.Errorf("and now this!")
+ })
+ t.Errorf("oh, and this too")
+ },
+ }, {
+ desc: "subtest calls fatal on parent",
+ ok: false,
+ output: `
+--- FAIL: subtest calls fatal on parent (N.NNs)
+ sub_test.go:NNN: first this
+ sub_test.go:NNN: and now this!
+ --- FAIL: subtest calls fatal on parent/#00 (N.NNs)
+ testing.go:NNN: test executed panic(nil) or runtime.Goexit: subtest may have called FailNow on a parent test`,
+ maxPar: 1,
+ f: func(t *T) {
+ outer := t
+ t.Errorf("first this")
+ t.Run("", func(t *T) {
+ outer.Fatalf("and now this!")
+ })
+ t.Errorf("Should not reach here.")
+ },
+ }, {
+ desc: "subtest calls error on ancestor",
+ ok: false,
+ output: `
+--- FAIL: subtest calls error on ancestor (N.NNs)
+ sub_test.go:NNN: Report to ancestor
+ --- FAIL: subtest calls error on ancestor/#00 (N.NNs)
+ sub_test.go:NNN: Still do this
+ sub_test.go:NNN: Also do this`,
+ maxPar: 1,
+ f: func(t *T) {
+ outer := t
+ t.Run("", func(t *T) {
+ t.Run("", func(t *T) {
+ outer.Errorf("Report to ancestor")
+ })
+ t.Errorf("Still do this")
+ })
+ t.Errorf("Also do this")
+ },
+ }, {
+ desc: "subtest calls fatal on ancestor",
+ ok: false,
+ output: `
+--- FAIL: subtest calls fatal on ancestor (N.NNs)
+ sub_test.go:NNN: Nope`,
+ maxPar: 1,
+ f: func(t *T) {
+ outer := t
+ t.Run("", func(t *T) {
+ for i := 0; i < 4; i++ {
+ t.Run("", func(t *T) {
+ outer.Fatalf("Nope")
+ })
+ t.Errorf("Don't do this")
+ }
+ t.Errorf("And neither do this")
+ })
+ t.Errorf("Nor this")
+ },
+ }, {
+ desc: "panic on goroutine fail after test exit",
+ ok: false,
+ maxPar: 4,
+ f: func(t *T) {
+ ch := make(chan bool)
+ t.Run("", func(t *T) {
+ go func() {
+ <-ch
+ defer func() {
+ if r := recover(); r == nil {
+ realTest.Errorf("expected panic")
+ }
+ ch <- true
+ }()
+ t.Errorf("failed after success")
+ }()
+ })
+ ch <- true
+ <-ch
+ },
+ }, {
+ desc: "log in finished sub test logs to parent",
+ ok: false,
+ output: `
+ --- FAIL: log in finished sub test logs to parent (N.NNs)
+ sub_test.go:NNN: message2
+ sub_test.go:NNN: message1
+ sub_test.go:NNN: error`,
+ maxPar: 1,
+ f: func(t *T) {
+ ch := make(chan bool)
+ t.Run("sub", func(t2 *T) {
+ go func() {
+ <-ch
+ t2.Log("message1")
+ ch <- true
+ }()
+ })
+ t.Log("message2")
+ ch <- true
+ <-ch
+ t.Errorf("error")
+ },
+ }, {
+ // A chatty test should always log with fmt.Print, even if the
+ // parent test has completed.
+ desc: "log in finished sub test with chatty",
+ ok: false,
+ chatty: true,
+ output: `
+ --- FAIL: log in finished sub test with chatty (N.NNs)`,
+ maxPar: 1,
+ f: func(t *T) {
+ ch := make(chan bool)
+ t.Run("sub", func(t2 *T) {
+ go func() {
+ <-ch
+ t2.Log("message1")
+ ch <- true
+ }()
+ })
+ t.Log("message2")
+ ch <- true
+ <-ch
+ t.Errorf("error")
+ },
+ }, {
+ // If a subtest panics we should run cleanups.
+ desc: "cleanup when subtest panics",
+ ok: false,
+ chatty: false,
+ output: `
+--- FAIL: cleanup when subtest panics (N.NNs)
+ --- FAIL: cleanup when subtest panics/sub (N.NNs)
+ sub_test.go:NNN: running cleanup`,
+ f: func(t *T) {
+ t.Cleanup(func() { t.Log("running cleanup") })
+ t.Run("sub", func(t2 *T) {
+ t2.FailNow()
+ })
+ },
+ }}
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *T) {
+ ctx := newTestContext(tc.maxPar, allMatcher())
+ buf := &strings.Builder{}
+ root := &T{
+ common: common{
+ signal: make(chan bool),
+ barrier: make(chan bool),
+ name: "",
+ w: buf,
+ },
+ context: ctx,
+ }
+ if tc.chatty {
+ root.chatty = newChattyPrinter(root.w)
+ root.chatty.json = tc.json
+ }
+ ok := root.Run(tc.desc, tc.f)
+ ctx.release()
+
+ if ok != tc.ok {
+ t.Errorf("%s:ok: got %v; want %v", tc.desc, ok, tc.ok)
+ }
+ if ok != !root.Failed() {
+ t.Errorf("%s:root failed: got %v; want %v", tc.desc, !ok, root.Failed())
+ }
+ if ctx.running != 0 || ctx.numWaiting != 0 {
+ t.Errorf("%s:running and waiting non-zero: got %d and %d", tc.desc, ctx.running, ctx.numWaiting)
+ }
+ got := strings.TrimSpace(buf.String())
+ want := strings.TrimSpace(tc.output)
+ re := makeRegexp(want)
+ if ok, err := regexp.MatchString(re, got); !ok || err != nil {
+ t.Errorf("%s:output:\ngot:\n%s\nwant:\n%s", tc.desc, got, want)
+ }
+ })
+ }
+}
+
+func TestBRun(t *T) {
+ work := func(b *B) {
+ for i := 0; i < b.N; i++ {
+ time.Sleep(time.Nanosecond)
+ }
+ }
+ testCases := []struct {
+ desc string
+ failed bool
+ chatty bool
+ output string
+ f func(*B)
+ }{{
+ desc: "simulate sequential run of subbenchmarks.",
+ f: func(b *B) {
+ b.Run("", func(b *B) { work(b) })
+ time1 := b.result.NsPerOp()
+ b.Run("", func(b *B) { work(b) })
+ time2 := b.result.NsPerOp()
+ if time1 >= time2 {
+ t.Errorf("no time spent in benchmark t1 >= t2 (%d >= %d)", time1, time2)
+ }
+ },
+ }, {
+ desc: "bytes set by all benchmarks",
+ f: func(b *B) {
+ b.Run("", func(b *B) { b.SetBytes(10); work(b) })
+ b.Run("", func(b *B) { b.SetBytes(10); work(b) })
+ if b.result.Bytes != 20 {
+ t.Errorf("bytes: got: %d; want 20", b.result.Bytes)
+ }
+ },
+ }, {
+ desc: "bytes set by some benchmarks",
+ // In this case the bytes result is meaningless, so it must be 0.
+ f: func(b *B) {
+ b.Run("", func(b *B) { b.SetBytes(10); work(b) })
+ b.Run("", func(b *B) { work(b) })
+ b.Run("", func(b *B) { b.SetBytes(10); work(b) })
+ if b.result.Bytes != 0 {
+ t.Errorf("bytes: got: %d; want 0", b.result.Bytes)
+ }
+ },
+ }, {
+ desc: "failure carried over to root",
+ failed: true,
+ output: "--- FAIL: root",
+ f: func(b *B) { b.Fail() },
+ }, {
+ desc: "skipping without message, chatty",
+ chatty: true,
+ output: "--- SKIP: root",
+ f: func(b *B) { b.SkipNow() },
+ }, {
+ desc: "chatty with recursion",
+ chatty: true,
+ f: func(b *B) {
+ b.Run("", func(b *B) {
+ b.Run("", func(b *B) {})
+ })
+ },
+ }, {
+ desc: "skipping without message, not chatty",
+ f: func(b *B) { b.SkipNow() },
+ }, {
+ desc: "skipping after error",
+ failed: true,
+ output: `
+--- FAIL: root
+ sub_test.go:NNN: an error
+ sub_test.go:NNN: skipped`,
+ f: func(b *B) {
+ b.Error("an error")
+ b.Skip("skipped")
+ },
+ }, {
+ desc: "memory allocation",
+ f: func(b *B) {
+ const bufSize = 256
+ alloc := func(b *B) {
+ var buf [bufSize]byte
+ for i := 0; i < b.N; i++ {
+ _ = append([]byte(nil), buf[:]...)
+ }
+ }
+ b.Run("", func(b *B) {
+ alloc(b)
+ b.ReportAllocs()
+ })
+ b.Run("", func(b *B) {
+ alloc(b)
+ b.ReportAllocs()
+ })
+ // runtime.MemStats sometimes reports more allocations than the
+ // benchmark is responsible for. Luckily the point of this test is
+ // to ensure that the results are not underreported, so we can
+ // simply verify the lower bound.
+ if got := b.result.MemAllocs; got < 2 {
+ t.Errorf("MemAllocs was %v; want 2", got)
+ }
+ if got := b.result.MemBytes; got < 2*bufSize {
+ t.Errorf("MemBytes was %v; want %v", got, 2*bufSize)
+ }
+ },
+ }, {
+ desc: "cleanup is called",
+ f: func(b *B) {
+ var calls, cleanups, innerCalls, innerCleanups int
+ b.Run("", func(b *B) {
+ calls++
+ b.Cleanup(func() {
+ cleanups++
+ })
+ b.Run("", func(b *B) {
+ b.Cleanup(func() {
+ innerCleanups++
+ })
+ innerCalls++
+ })
+ work(b)
+ })
+ if calls == 0 || calls != cleanups {
+ t.Errorf("mismatched cleanups; got %d want %d", cleanups, calls)
+ }
+ if innerCalls == 0 || innerCalls != innerCleanups {
+ t.Errorf("mismatched cleanups; got %d want %d", cleanups, calls)
+ }
+ },
+ }, {
+ desc: "cleanup is called on failure",
+ failed: true,
+ f: func(b *B) {
+ var calls, cleanups int
+ b.Run("", func(b *B) {
+ calls++
+ b.Cleanup(func() {
+ cleanups++
+ })
+ b.Fatalf("failure")
+ })
+ if calls == 0 || calls != cleanups {
+ t.Errorf("mismatched cleanups; got %d want %d", cleanups, calls)
+ }
+ },
+ }}
+ hideStdoutForTesting = true
+ defer func() {
+ hideStdoutForTesting = false
+ }()
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *T) {
+ var ok bool
+ buf := &strings.Builder{}
+ // This is almost like the Benchmark function, except that we override
+ // the benchtime and catch the failure result of the subbenchmark.
+ root := &B{
+ common: common{
+ signal: make(chan bool),
+ name: "root",
+ w: buf,
+ },
+ benchFunc: func(b *B) { ok = b.Run("test", tc.f) }, // Use Run to catch failure.
+ benchTime: durationOrCountFlag{d: 1 * time.Microsecond},
+ }
+ if tc.chatty {
+ root.chatty = newChattyPrinter(root.w)
+ }
+ root.runN(1)
+ if ok != !tc.failed {
+ t.Errorf("%s:ok: got %v; want %v", tc.desc, ok, !tc.failed)
+ }
+ if !ok != root.Failed() {
+ t.Errorf("%s:root failed: got %v; want %v", tc.desc, !ok, root.Failed())
+ }
+ // All tests are run as subtests
+ if root.result.N != 1 {
+ t.Errorf("%s: N for parent benchmark was %d; want 1", tc.desc, root.result.N)
+ }
+ got := strings.TrimSpace(buf.String())
+ want := strings.TrimSpace(tc.output)
+ re := makeRegexp(want)
+ if ok, err := regexp.MatchString(re, got); !ok || err != nil {
+ t.Errorf("%s:output:\ngot:\n%s\nwant:\n%s", tc.desc, got, want)
+ }
+ })
+ }
+}
+
+func makeRegexp(s string) string {
+ s = regexp.QuoteMeta(s)
+ s = strings.ReplaceAll(s, "^V", "\x16")
+ s = strings.ReplaceAll(s, ":NNN:", `:\d\d\d\d?:`)
+ s = strings.ReplaceAll(s, "N\\.NNs", `\d*\.\d*s`)
+ return s
+}
+
+func TestBenchmarkOutput(t *T) {
+ // Ensure Benchmark initialized common.w by invoking it with an error and
+ // normal case.
+ Benchmark(func(b *B) { b.Error("do not print this output") })
+ Benchmark(func(b *B) {})
+}
+
+func TestBenchmarkStartsFrom1(t *T) {
+ var first = true
+ Benchmark(func(b *B) {
+ if first && b.N != 1 {
+ panic(fmt.Sprintf("Benchmark() first N=%v; want 1", b.N))
+ }
+ first = false
+ })
+}
+
+func TestBenchmarkReadMemStatsBeforeFirstRun(t *T) {
+ var first = true
+ Benchmark(func(b *B) {
+ if first && (b.startAllocs == 0 || b.startBytes == 0) {
+ panic("ReadMemStats not called before first run")
+ }
+ first = false
+ })
+}
+
+type funcWriter struct {
+ write func([]byte) (int, error)
+}
+
+func (fw *funcWriter) Write(b []byte) (int, error) {
+ return fw.write(b)
+}
+
+func TestRacyOutput(t *T) {
+ var runs int32 // The number of running Writes
+ var races int32 // Incremented for each race detected
+ raceDetector := func(b []byte) (int, error) {
+ // Check if some other goroutine is concurrently calling Write.
+ if atomic.LoadInt32(&runs) > 0 {
+ atomic.AddInt32(&races, 1) // Race detected!
+ }
+ atomic.AddInt32(&runs, 1)
+ defer atomic.AddInt32(&runs, -1)
+ runtime.Gosched() // Increase probability of a race
+ return len(b), nil
+ }
+
+ root := &T{
+ common: common{w: &funcWriter{raceDetector}},
+ context: newTestContext(1, allMatcher()),
+ }
+ root.chatty = newChattyPrinter(root.w)
+ root.Run("", func(t *T) {
+ var wg sync.WaitGroup
+ for i := 0; i < 100; i++ {
+ wg.Add(1)
+ go func(i int) {
+ defer wg.Done()
+ t.Run(fmt.Sprint(i), func(t *T) {
+ t.Logf("testing run %d", i)
+ })
+ }(i)
+ }
+ wg.Wait()
+ })
+
+ if races > 0 {
+ t.Errorf("detected %d racy Writes", races)
+ }
+}
+
+// The late log message did not include the test name. Issue 29388.
+func TestLogAfterComplete(t *T) {
+ ctx := newTestContext(1, allMatcher())
+ var buf bytes.Buffer
+ t1 := &T{
+ common: common{
+ // Use a buffered channel so that tRunner can write
+ // to it although nothing is reading from it.
+ signal: make(chan bool, 1),
+ w: &buf,
+ },
+ context: ctx,
+ }
+
+ c1 := make(chan bool)
+ c2 := make(chan string)
+ tRunner(t1, func(t *T) {
+ t.Run("TestLateLog", func(t *T) {
+ go func() {
+ defer close(c2)
+ defer func() {
+ p := recover()
+ if p == nil {
+ c2 <- "subtest did not panic"
+ return
+ }
+ s, ok := p.(string)
+ if !ok {
+ c2 <- fmt.Sprintf("subtest panic with unexpected value %v", p)
+ return
+ }
+ const want = "Log in goroutine after TestLateLog has completed: log after test"
+ if !strings.Contains(s, want) {
+ c2 <- fmt.Sprintf("subtest panic %q does not contain %q", s, want)
+ }
+ }()
+
+ <-c1
+ t.Log("log after test")
+ }()
+ })
+ })
+ close(c1)
+
+ if s := <-c2; s != "" {
+ t.Error(s)
+ }
+}
+
+func TestBenchmark(t *T) {
+ if Short() {
+ t.Skip("skipping in short mode")
+ }
+ res := Benchmark(func(b *B) {
+ for i := 0; i < 5; i++ {
+ b.Run("", func(b *B) {
+ for i := 0; i < b.N; i++ {
+ time.Sleep(time.Millisecond)
+ }
+ })
+ }
+ })
+ if res.NsPerOp() < 4000000 {
+ t.Errorf("want >5ms; got %v", time.Duration(res.NsPerOp()))
+ }
+}
+
+func TestCleanup(t *T) {
+ var cleanups []int
+ t.Run("test", func(t *T) {
+ t.Cleanup(func() { cleanups = append(cleanups, 1) })
+ t.Cleanup(func() { cleanups = append(cleanups, 2) })
+ })
+ if got, want := cleanups, []int{2, 1}; !reflect.DeepEqual(got, want) {
+ t.Errorf("unexpected cleanup record; got %v want %v", got, want)
+ }
+}
+
+func TestConcurrentCleanup(t *T) {
+ cleanups := 0
+ t.Run("test", func(t *T) {
+ var wg sync.WaitGroup
+ wg.Add(2)
+ for i := 0; i < 2; i++ {
+ i := i
+ go func() {
+ t.Cleanup(func() {
+ // Although the calls to Cleanup are concurrent, the functions passed
+ // to Cleanup should be called sequentially, in some nondeterministic
+ // order based on when the Cleanup calls happened to be scheduled.
+ // So these assignments to the cleanups variable should not race.
+ cleanups |= 1 << i
+ })
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+ })
+ if cleanups != 1|2 {
+ t.Errorf("unexpected cleanup; got %d want 3", cleanups)
+ }
+}
+
+func TestCleanupCalledEvenAfterGoexit(t *T) {
+ cleanups := 0
+ t.Run("test", func(t *T) {
+ t.Cleanup(func() {
+ cleanups++
+ })
+ t.Cleanup(func() {
+ runtime.Goexit()
+ })
+ })
+ if cleanups != 1 {
+ t.Errorf("unexpected cleanup count; got %d want 1", cleanups)
+ }
+}
+
+func TestRunCleanup(t *T) {
+ outerCleanup := 0
+ innerCleanup := 0
+ t.Run("test", func(t *T) {
+ t.Cleanup(func() { outerCleanup++ })
+ t.Run("x", func(t *T) {
+ t.Cleanup(func() { innerCleanup++ })
+ })
+ })
+ if innerCleanup != 1 {
+ t.Errorf("unexpected inner cleanup count; got %d want 1", innerCleanup)
+ }
+ if outerCleanup != 1 {
+ t.Errorf("unexpected outer cleanup count; got %d want 0", outerCleanup)
+ }
+}
+
+func TestCleanupParallelSubtests(t *T) {
+ ranCleanup := 0
+ t.Run("test", func(t *T) {
+ t.Cleanup(func() { ranCleanup++ })
+ t.Run("x", func(t *T) {
+ t.Parallel()
+ if ranCleanup > 0 {
+ t.Error("outer cleanup ran before parallel subtest")
+ }
+ })
+ })
+ if ranCleanup != 1 {
+ t.Errorf("unexpected cleanup count; got %d want 1", ranCleanup)
+ }
+}
+
+func TestNestedCleanup(t *T) {
+ ranCleanup := 0
+ t.Run("test", func(t *T) {
+ t.Cleanup(func() {
+ if ranCleanup != 2 {
+ t.Errorf("unexpected cleanup count in first cleanup: got %d want 2", ranCleanup)
+ }
+ ranCleanup++
+ })
+ t.Cleanup(func() {
+ if ranCleanup != 0 {
+ t.Errorf("unexpected cleanup count in second cleanup: got %d want 0", ranCleanup)
+ }
+ ranCleanup++
+ t.Cleanup(func() {
+ if ranCleanup != 1 {
+ t.Errorf("unexpected cleanup count in nested cleanup: got %d want 1", ranCleanup)
+ }
+ ranCleanup++
+ })
+ })
+ })
+ if ranCleanup != 3 {
+ t.Errorf("unexpected cleanup count: got %d want 3", ranCleanup)
+ }
+}
diff --git a/src/testing/testing.go b/src/testing/testing.go
new file mode 100644
index 0000000..5c06aea
--- /dev/null
+++ b/src/testing/testing.go
@@ -0,0 +1,2409 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package testing provides support for automated testing of Go packages.
+// It is intended to be used in concert with the "go test" command, which automates
+// execution of any function of the form
+//
+// func TestXxx(*testing.T)
+//
+// where Xxx does not start with a lowercase letter. The function name
+// serves to identify the test routine.
+//
+// Within these functions, use the Error, Fail or related methods to signal failure.
+//
+// To write a new test suite, create a file that
+// contains the TestXxx functions as described here,
+// and give that file a name ending in "_test.go".
+// The file will be excluded from regular
+// package builds but will be included when the "go test" command is run.
+//
+// The test file can be in the same package as the one being tested,
+// or in a corresponding package with the suffix "_test".
+//
+// If the test file is in the same package, it may refer to unexported
+// identifiers within the package, as in this example:
+//
+// package abs
+//
+// import "testing"
+//
+// func TestAbs(t *testing.T) {
+// got := Abs(-1)
+// if got != 1 {
+// t.Errorf("Abs(-1) = %d; want 1", got)
+// }
+// }
+//
+// If the file is in a separate "_test" package, the package being tested
+// must be imported explicitly and only its exported identifiers may be used.
+// This is known as "black box" testing.
+//
+// package abs_test
+//
+// import (
+// "testing"
+//
+// "path_to_pkg/abs"
+// )
+//
+// func TestAbs(t *testing.T) {
+// got := abs.Abs(-1)
+// if got != 1 {
+// t.Errorf("Abs(-1) = %d; want 1", got)
+// }
+// }
+//
+// For more detail, run "go help test" and "go help testflag".
+//
+// # Benchmarks
+//
+// Functions of the form
+//
+// func BenchmarkXxx(*testing.B)
+//
+// are considered benchmarks, and are executed by the "go test" command when
+// its -bench flag is provided. Benchmarks are run sequentially.
+//
+// For a description of the testing flags, see
+// https://golang.org/cmd/go/#hdr-Testing_flags.
+//
+// A sample benchmark function looks like this:
+//
+// func BenchmarkRandInt(b *testing.B) {
+// for i := 0; i < b.N; i++ {
+// rand.Int()
+// }
+// }
+//
+// The benchmark function must run the target code b.N times.
+// During benchmark execution, b.N is adjusted until the benchmark function lasts
+// long enough to be timed reliably. The output
+//
+// BenchmarkRandInt-8 68453040 17.8 ns/op
+//
+// means that the loop ran 68453040 times at a speed of 17.8 ns per loop.
+//
+// If a benchmark needs some expensive setup before running, the timer
+// may be reset:
+//
+// func BenchmarkBigLen(b *testing.B) {
+// big := NewBig()
+// b.ResetTimer()
+// for i := 0; i < b.N; i++ {
+// big.Len()
+// }
+// }
+//
+// If a benchmark needs to test performance in a parallel setting, it may use
+// the RunParallel helper function; such benchmarks are intended to be used with
+// the go test -cpu flag:
+//
+// func BenchmarkTemplateParallel(b *testing.B) {
+// templ := template.Must(template.New("test").Parse("Hello, {{.}}!"))
+// b.RunParallel(func(pb *testing.PB) {
+// var buf bytes.Buffer
+// for pb.Next() {
+// buf.Reset()
+// templ.Execute(&buf, "World")
+// }
+// })
+// }
+//
+// A detailed specification of the benchmark results format is given
+// in https://golang.org/design/14313-benchmark-format.
+//
+// There are standard tools for working with benchmark results at
+// https://golang.org/x/perf/cmd.
+// In particular, https://golang.org/x/perf/cmd/benchstat performs
+// statistically robust A/B comparisons.
+//
+// # Examples
+//
+// The package also runs and verifies example code. Example functions may
+// include a concluding line comment that begins with "Output:" and is compared with
+// the standard output of the function when the tests are run. (The comparison
+// ignores leading and trailing space.) These are examples of an example:
+//
+// func ExampleHello() {
+// fmt.Println("hello")
+// // Output: hello
+// }
+//
+// func ExampleSalutations() {
+// fmt.Println("hello, and")
+// fmt.Println("goodbye")
+// // Output:
+// // hello, and
+// // goodbye
+// }
+//
+// The comment prefix "Unordered output:" is like "Output:", but matches any
+// line order:
+//
+// func ExamplePerm() {
+// for _, value := range Perm(5) {
+// fmt.Println(value)
+// }
+// // Unordered output: 4
+// // 2
+// // 1
+// // 3
+// // 0
+// }
+//
+// Example functions without output comments are compiled but not executed.
+//
+// The naming convention to declare examples for the package, a function F, a type T and
+// method M on type T are:
+//
+// func Example() { ... }
+// func ExampleF() { ... }
+// func ExampleT() { ... }
+// func ExampleT_M() { ... }
+//
+// Multiple example functions for a package/type/function/method may be provided by
+// appending a distinct suffix to the name. The suffix must start with a
+// lower-case letter.
+//
+// func Example_suffix() { ... }
+// func ExampleF_suffix() { ... }
+// func ExampleT_suffix() { ... }
+// func ExampleT_M_suffix() { ... }
+//
+// The entire test file is presented as the example when it contains a single
+// example function, at least one other function, type, variable, or constant
+// declaration, and no test or benchmark functions.
+//
+// # Fuzzing
+//
+// 'go test' and the testing package support fuzzing, a testing technique where
+// a function is called with randomly generated inputs to find bugs not
+// anticipated by unit tests.
+//
+// Functions of the form
+//
+// func FuzzXxx(*testing.F)
+//
+// are considered fuzz tests.
+//
+// For example:
+//
+// func FuzzHex(f *testing.F) {
+// for _, seed := range [][]byte{{}, {0}, {9}, {0xa}, {0xf}, {1, 2, 3, 4}} {
+// f.Add(seed)
+// }
+// f.Fuzz(func(t *testing.T, in []byte) {
+// enc := hex.EncodeToString(in)
+// out, err := hex.DecodeString(enc)
+// if err != nil {
+// t.Fatalf("%v: decode: %v", in, err)
+// }
+// if !bytes.Equal(in, out) {
+// t.Fatalf("%v: not equal after round trip: %v", in, out)
+// }
+// })
+// }
+//
+// A fuzz test maintains a seed corpus, or a set of inputs which are run by
+// default, and can seed input generation. Seed inputs may be registered by
+// calling (*F).Add or by storing files in the directory testdata/fuzz/<Name>
+// (where <Name> is the name of the fuzz test) within the package containing
+// the fuzz test. Seed inputs are optional, but the fuzzing engine may find
+// bugs more efficiently when provided with a set of small seed inputs with good
+// code coverage. These seed inputs can also serve as regression tests for bugs
+// identified through fuzzing.
+//
+// The function passed to (*F).Fuzz within the fuzz test is considered the fuzz
+// target. A fuzz target must accept a *T parameter, followed by one or more
+// parameters for random inputs. The types of arguments passed to (*F).Add must
+// be identical to the types of these parameters. The fuzz target may signal
+// that it's found a problem the same way tests do: by calling T.Fail (or any
+// method that calls it like T.Error or T.Fatal) or by panicking.
+//
+// When fuzzing is enabled (by setting the -fuzz flag to a regular expression
+// that matches a specific fuzz test), the fuzz target is called with arguments
+// generated by repeatedly making random changes to the seed inputs. On
+// supported platforms, 'go test' compiles the test executable with fuzzing
+// coverage instrumentation. The fuzzing engine uses that instrumentation to
+// find and cache inputs that expand coverage, increasing the likelihood of
+// finding bugs. If the fuzz target fails for a given input, the fuzzing engine
+// writes the inputs that caused the failure to a file in the directory
+// testdata/fuzz/<Name> within the package directory. This file later serves as
+// a seed input. If the file can't be written at that location (for example,
+// because the directory is read-only), the fuzzing engine writes the file to
+// the fuzz cache directory within the build cache instead.
+//
+// When fuzzing is disabled, the fuzz target is called with the seed inputs
+// registered with F.Add and seed inputs from testdata/fuzz/<Name>. In this
+// mode, the fuzz test acts much like a regular test, with subtests started
+// with F.Fuzz instead of T.Run.
+//
+// See https://go.dev/doc/fuzz for documentation about fuzzing.
+//
+// # Skipping
+//
+// Tests or benchmarks may be skipped at run time with a call to
+// the Skip method of *T or *B:
+//
+// func TestTimeConsuming(t *testing.T) {
+// if testing.Short() {
+// t.Skip("skipping test in short mode.")
+// }
+// ...
+// }
+//
+// The Skip method of *T can be used in a fuzz target if the input is invalid,
+// but should not be considered a failing input. For example:
+//
+// func FuzzJSONMarshaling(f *testing.F) {
+// f.Fuzz(func(t *testing.T, b []byte) {
+// var v interface{}
+// if err := json.Unmarshal(b, &v); err != nil {
+// t.Skip()
+// }
+// if _, err := json.Marshal(v); err != nil {
+// t.Errorf("Marshal: %v", err)
+// }
+// })
+// }
+//
+// # Subtests and Sub-benchmarks
+//
+// The Run methods of T and B allow defining subtests and sub-benchmarks,
+// without having to define separate functions for each. This enables uses
+// like table-driven benchmarks and creating hierarchical tests.
+// It also provides a way to share common setup and tear-down code:
+//
+// func TestFoo(t *testing.T) {
+// // <setup code>
+// t.Run("A=1", func(t *testing.T) { ... })
+// t.Run("A=2", func(t *testing.T) { ... })
+// t.Run("B=1", func(t *testing.T) { ... })
+// // <tear-down code>
+// }
+//
+// Each subtest and sub-benchmark has a unique name: the combination of the name
+// of the top-level test and the sequence of names passed to Run, separated by
+// slashes, with an optional trailing sequence number for disambiguation.
+//
+// The argument to the -run, -bench, and -fuzz command-line flags is an unanchored regular
+// expression that matches the test's name. For tests with multiple slash-separated
+// elements, such as subtests, the argument is itself slash-separated, with
+// expressions matching each name element in turn. Because it is unanchored, an
+// empty expression matches any string.
+// For example, using "matching" to mean "whose name contains":
+//
+// go test -run '' # Run all tests.
+// go test -run Foo # Run top-level tests matching "Foo", such as "TestFooBar".
+// go test -run Foo/A= # For top-level tests matching "Foo", run subtests matching "A=".
+// go test -run /A=1 # For all top-level tests, run subtests matching "A=1".
+// go test -fuzz FuzzFoo # Fuzz the target matching "FuzzFoo"
+//
+// The -run argument can also be used to run a specific value in the seed
+// corpus, for debugging. For example:
+//
+// go test -run=FuzzFoo/9ddb952d9814
+//
+// The -fuzz and -run flags can both be set, in order to fuzz a target but
+// skip the execution of all other tests.
+//
+// Subtests can also be used to control parallelism. A parent test will only
+// complete once all of its subtests complete. In this example, all tests are
+// run in parallel with each other, and only with each other, regardless of
+// other top-level tests that may be defined:
+//
+// func TestGroupedParallel(t *testing.T) {
+// for _, tc := range tests {
+// tc := tc // capture range variable
+// t.Run(tc.Name, func(t *testing.T) {
+// t.Parallel()
+// ...
+// })
+// }
+// }
+//
+// Run does not return until parallel subtests have completed, providing a way
+// to clean up after a group of parallel tests:
+//
+// func TestTeardownParallel(t *testing.T) {
+// // This Run will not return until the parallel tests finish.
+// t.Run("group", func(t *testing.T) {
+// t.Run("Test1", parallelTest1)
+// t.Run("Test2", parallelTest2)
+// t.Run("Test3", parallelTest3)
+// })
+// // <tear-down code>
+// }
+//
+// # Main
+//
+// It is sometimes necessary for a test or benchmark program to do extra setup or teardown
+// before or after it executes. It is also sometimes necessary to control
+// which code runs on the main thread. To support these and other cases,
+// if a test file contains a function:
+//
+// func TestMain(m *testing.M)
+//
+// then the generated test will call TestMain(m) instead of running the tests or benchmarks
+// directly. TestMain runs in the main goroutine and can do whatever setup
+// and teardown is necessary around a call to m.Run. m.Run will return an exit
+// code that may be passed to os.Exit. If TestMain returns, the test wrapper
+// will pass the result of m.Run to os.Exit itself.
+//
+// When TestMain is called, flag.Parse has not been run. If TestMain depends on
+// command-line flags, including those of the testing package, it should call
+// flag.Parse explicitly. Command line flags are always parsed by the time test
+// or benchmark functions run.
+//
+// A simple implementation of TestMain is:
+//
+// func TestMain(m *testing.M) {
+// // call flag.Parse() here if TestMain uses flags
+// os.Exit(m.Run())
+// }
+//
+// TestMain is a low-level primitive and should not be necessary for casual
+// testing needs, where ordinary test functions suffice.
+package testing
+
+import (
+ "bytes"
+ "errors"
+ "flag"
+ "fmt"
+ "internal/goexperiment"
+ "internal/race"
+ "io"
+ "math/rand"
+ "os"
+ "reflect"
+ "runtime"
+ "runtime/debug"
+ "runtime/trace"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "time"
+ "unicode"
+ "unicode/utf8"
+)
+
+var initRan bool
+
+// Init registers testing flags. These flags are automatically registered by
+// the "go test" command before running test functions, so Init is only needed
+// when calling functions such as Benchmark without using "go test".
+//
+// Init is not safe to call concurrently. It has no effect if it was already called.
+func Init() {
+ if initRan {
+ return
+ }
+ initRan = true
+ // The short flag requests that tests run more quickly, but its functionality
+ // is provided by test writers themselves. The testing package is just its
+ // home. The all.bash installation script sets it to make installation more
+ // efficient, but by default the flag is off so a plain "go test" will do a
+ // full test of the package.
+ short = flag.Bool("test.short", false, "run smaller test suite to save time")
+
+ // The failfast flag requests that test execution stop after the first test failure.
+ failFast = flag.Bool("test.failfast", false, "do not start new tests after the first test failure")
+
+ // The directory in which to create profile files and the like. When run from
+ // "go test", the binary always runs in the source directory for the package;
+ // this flag lets "go test" tell the binary to write the files in the directory where
+ // the "go test" command is run.
+ outputDir = flag.String("test.outputdir", "", "write profiles to `dir`")
+ // Report as tests are run; default is silent for success.
+ flag.Var(&chatty, "test.v", "verbose: print additional output")
+ count = flag.Uint("test.count", 1, "run tests and benchmarks `n` times")
+ coverProfile = flag.String("test.coverprofile", "", "write a coverage profile to `file`")
+ gocoverdir = flag.String("test.gocoverdir", "", "write coverage intermediate files to this directory")
+ matchList = flag.String("test.list", "", "list tests, examples, and benchmarks matching `regexp` then exit")
+ match = flag.String("test.run", "", "run only tests and examples matching `regexp`")
+ skip = flag.String("test.skip", "", "do not list or run tests matching `regexp`")
+ memProfile = flag.String("test.memprofile", "", "write an allocation profile to `file`")
+ memProfileRate = flag.Int("test.memprofilerate", 0, "set memory allocation profiling `rate` (see runtime.MemProfileRate)")
+ cpuProfile = flag.String("test.cpuprofile", "", "write a cpu profile to `file`")
+ blockProfile = flag.String("test.blockprofile", "", "write a goroutine blocking profile to `file`")
+ blockProfileRate = flag.Int("test.blockprofilerate", 1, "set blocking profile `rate` (see runtime.SetBlockProfileRate)")
+ mutexProfile = flag.String("test.mutexprofile", "", "write a mutex contention profile to the named file after execution")
+ mutexProfileFraction = flag.Int("test.mutexprofilefraction", 1, "if >= 0, calls runtime.SetMutexProfileFraction()")
+ panicOnExit0 = flag.Bool("test.paniconexit0", false, "panic on call to os.Exit(0)")
+ traceFile = flag.String("test.trace", "", "write an execution trace to `file`")
+ timeout = flag.Duration("test.timeout", 0, "panic test binary after duration `d` (default 0, timeout disabled)")
+ cpuListStr = flag.String("test.cpu", "", "comma-separated `list` of cpu counts to run each test with")
+ parallel = flag.Int("test.parallel", runtime.GOMAXPROCS(0), "run at most `n` tests in parallel")
+ testlog = flag.String("test.testlogfile", "", "write test action log to `file` (for use only by cmd/go)")
+ shuffle = flag.String("test.shuffle", "off", "randomize the execution order of tests and benchmarks")
+ fullPath = flag.Bool("test.fullpath", false, "show full file names in error messages")
+
+ initBenchmarkFlags()
+ initFuzzFlags()
+}
+
+var (
+ // Flags, registered during Init.
+ short *bool
+ failFast *bool
+ outputDir *string
+ chatty chattyFlag
+ count *uint
+ coverProfile *string
+ gocoverdir *string
+ matchList *string
+ match *string
+ skip *string
+ memProfile *string
+ memProfileRate *int
+ cpuProfile *string
+ blockProfile *string
+ blockProfileRate *int
+ mutexProfile *string
+ mutexProfileFraction *int
+ panicOnExit0 *bool
+ traceFile *string
+ timeout *time.Duration
+ cpuListStr *string
+ parallel *int
+ shuffle *string
+ testlog *string
+ fullPath *bool
+
+ haveExamples bool // are there examples?
+
+ cpuList []int
+ testlogFile *os.File
+
+ numFailed atomic.Uint32 // number of test failures
+
+ running sync.Map // map[string]time.Time of running, unpaused tests
+)
+
+type chattyFlag struct {
+ on bool // -v is set in some form
+ json bool // -v=test2json is set, to make output better for test2json
+}
+
+func (*chattyFlag) IsBoolFlag() bool { return true }
+
+func (f *chattyFlag) Set(arg string) error {
+ switch arg {
+ default:
+ return fmt.Errorf("invalid flag -test.v=%s", arg)
+ case "true", "test2json":
+ f.on = true
+ f.json = arg == "test2json"
+ case "false":
+ f.on = false
+ f.json = false
+ }
+ return nil
+}
+
+func (f *chattyFlag) String() string {
+ if f.json {
+ return "test2json"
+ }
+ if f.on {
+ return "true"
+ }
+ return "false"
+}
+
+func (f *chattyFlag) Get() any {
+ if f.json {
+ return "test2json"
+ }
+ return f.on
+}
+
+const marker = byte(0x16) // ^V for framing
+
+func (f *chattyFlag) prefix() string {
+ if f.json {
+ return string(marker)
+ }
+ return ""
+}
+
+type chattyPrinter struct {
+ w io.Writer
+ lastNameMu sync.Mutex // guards lastName
+ lastName string // last printed test name in chatty mode
+ json bool // -v=json output mode
+}
+
+func newChattyPrinter(w io.Writer) *chattyPrinter {
+ return &chattyPrinter{w: w, json: chatty.json}
+}
+
+// prefix is like chatty.prefix but using p.json instead of chatty.json.
+// Using p.json allows tests to check the json behavior without modifying
+// the global variable. For convenience, we allow p == nil and treat
+// that as not in json mode (because it's not chatty at all).
+func (p *chattyPrinter) prefix() string {
+ if p != nil && p.json {
+ return string(marker)
+ }
+ return ""
+}
+
+// Updatef prints a message about the status of the named test to w.
+//
+// The formatted message must include the test name itself.
+func (p *chattyPrinter) Updatef(testName, format string, args ...any) {
+ p.lastNameMu.Lock()
+ defer p.lastNameMu.Unlock()
+
+ // Since the message already implies an association with a specific new test,
+ // we don't need to check what the old test name was or log an extra NAME line
+ // for it. (We're updating it anyway, and the current message already includes
+ // the test name.)
+ p.lastName = testName
+ fmt.Fprintf(p.w, p.prefix()+format, args...)
+}
+
+// Printf prints a message, generated by the named test, that does not
+// necessarily mention that tests's name itself.
+func (p *chattyPrinter) Printf(testName, format string, args ...any) {
+ p.lastNameMu.Lock()
+ defer p.lastNameMu.Unlock()
+
+ if p.lastName == "" {
+ p.lastName = testName
+ } else if p.lastName != testName {
+ fmt.Fprintf(p.w, "%s=== NAME %s\n", p.prefix(), testName)
+ p.lastName = testName
+ }
+
+ fmt.Fprintf(p.w, format, args...)
+}
+
+// The maximum number of stack frames to go through when skipping helper functions for
+// the purpose of decorating log messages.
+const maxStackLen = 50
+
+// common holds the elements common between T and B and
+// captures common methods such as Errorf.
+type common struct {
+ mu sync.RWMutex // guards this group of fields
+ output []byte // Output generated by test or benchmark.
+ w io.Writer // For flushToParent.
+ ran bool // Test or benchmark (or one of its subtests) was executed.
+ failed bool // Test or benchmark has failed.
+ skipped bool // Test or benchmark has been skipped.
+ done bool // Test is finished and all subtests have completed.
+ helperPCs map[uintptr]struct{} // functions to be skipped when writing file/line info
+ helperNames map[string]struct{} // helperPCs converted to function names
+ cleanups []func() // optional functions to be called at the end of the test
+ cleanupName string // Name of the cleanup function.
+ cleanupPc []uintptr // The stack trace at the point where Cleanup was called.
+ finished bool // Test function has completed.
+ inFuzzFn bool // Whether the fuzz target, if this is one, is running.
+
+ chatty *chattyPrinter // A copy of chattyPrinter, if the chatty flag is set.
+ bench bool // Whether the current test is a benchmark.
+ hasSub atomic.Bool // whether there are sub-benchmarks.
+ cleanupStarted atomic.Bool // Registered cleanup callbacks have started to execute
+ runner string // Function name of tRunner running the test.
+ isParallel bool // Whether the test is parallel.
+
+ parent *common
+ level int // Nesting depth of test or benchmark.
+ creator []uintptr // If level > 0, the stack trace at the point where the parent called t.Run.
+ name string // Name of test or benchmark.
+ start time.Time // Time test or benchmark started
+ duration time.Duration
+ barrier chan bool // To signal parallel subtests they may start. Nil when T.Parallel is not present (B) or not usable (when fuzzing).
+ signal chan bool // To signal a test is done.
+ sub []*T // Queue of subtests to be run in parallel.
+
+ lastRaceErrors atomic.Int64 // Max value of race.Errors seen during the test or its subtests.
+ raceErrorLogged atomic.Bool
+
+ tempDirMu sync.Mutex
+ tempDir string
+ tempDirErr error
+ tempDirSeq int32
+}
+
+// Short reports whether the -test.short flag is set.
+func Short() bool {
+ if short == nil {
+ panic("testing: Short called before Init")
+ }
+ // Catch code that calls this from TestMain without first calling flag.Parse.
+ if !flag.Parsed() {
+ panic("testing: Short called before Parse")
+ }
+
+ return *short
+}
+
+// testBinary is set by cmd/go to "1" if this is a binary built by "go test".
+// The value is set to "1" by a -X option to cmd/link. We assume that
+// because this is possible, the compiler will not optimize testBinary
+// into a constant on the basis that it is an unexported package-scope
+// variable that is never changed. If the compiler ever starts implementing
+// such an optimization, we will need some technique to mark this variable
+// as "changed by a cmd/link -X option".
+var testBinary = "0"
+
+// Testing reports whether the current code is being run in a test.
+// This will report true in programs created by "go test",
+// false in programs created by "go build".
+func Testing() bool {
+ return testBinary == "1"
+}
+
+// CoverMode reports what the test coverage mode is set to. The
+// values are "set", "count", or "atomic". The return value will be
+// empty if test coverage is not enabled.
+func CoverMode() string {
+ if goexperiment.CoverageRedesign {
+ return cover2.mode
+ }
+ return cover.Mode
+}
+
+// Verbose reports whether the -test.v flag is set.
+func Verbose() bool {
+ // Same as in Short.
+ if !flag.Parsed() {
+ panic("testing: Verbose called before Parse")
+ }
+ return chatty.on
+}
+
+func (c *common) checkFuzzFn(name string) {
+ if c.inFuzzFn {
+ panic(fmt.Sprintf("testing: f.%s was called inside the fuzz target, use t.%s instead", name, name))
+ }
+}
+
+// frameSkip searches, starting after skip frames, for the first caller frame
+// in a function not marked as a helper and returns that frame.
+// The search stops if it finds a tRunner function that
+// was the entry point into the test and the test is not a subtest.
+// This function must be called with c.mu held.
+func (c *common) frameSkip(skip int) runtime.Frame {
+ // If the search continues into the parent test, we'll have to hold
+ // its mu temporarily. If we then return, we need to unlock it.
+ shouldUnlock := false
+ defer func() {
+ if shouldUnlock {
+ c.mu.Unlock()
+ }
+ }()
+ var pc [maxStackLen]uintptr
+ // Skip two extra frames to account for this function
+ // and runtime.Callers itself.
+ n := runtime.Callers(skip+2, pc[:])
+ if n == 0 {
+ panic("testing: zero callers found")
+ }
+ frames := runtime.CallersFrames(pc[:n])
+ var firstFrame, prevFrame, frame runtime.Frame
+ for more := true; more; prevFrame = frame {
+ frame, more = frames.Next()
+ if frame.Function == "runtime.gopanic" {
+ continue
+ }
+ if frame.Function == c.cleanupName {
+ frames = runtime.CallersFrames(c.cleanupPc)
+ continue
+ }
+ if firstFrame.PC == 0 {
+ firstFrame = frame
+ }
+ if frame.Function == c.runner {
+ // We've gone up all the way to the tRunner calling
+ // the test function (so the user must have
+ // called tb.Helper from inside that test function).
+ // If this is a top-level test, only skip up to the test function itself.
+ // If we're in a subtest, continue searching in the parent test,
+ // starting from the point of the call to Run which created this subtest.
+ if c.level > 1 {
+ frames = runtime.CallersFrames(c.creator)
+ parent := c.parent
+ // We're no longer looking at the current c after this point,
+ // so we should unlock its mu, unless it's the original receiver,
+ // in which case our caller doesn't expect us to do that.
+ if shouldUnlock {
+ c.mu.Unlock()
+ }
+ c = parent
+ // Remember to unlock c.mu when we no longer need it, either
+ // because we went up another nesting level, or because we
+ // returned.
+ shouldUnlock = true
+ c.mu.Lock()
+ continue
+ }
+ return prevFrame
+ }
+ // If more helper PCs have been added since we last did the conversion
+ if c.helperNames == nil {
+ c.helperNames = make(map[string]struct{})
+ for pc := range c.helperPCs {
+ c.helperNames[pcToName(pc)] = struct{}{}
+ }
+ }
+ if _, ok := c.helperNames[frame.Function]; !ok {
+ // Found a frame that wasn't inside a helper function.
+ return frame
+ }
+ }
+ return firstFrame
+}
+
+// decorate prefixes the string with the file and line of the call site
+// and inserts the final newline if needed and indentation spaces for formatting.
+// This function must be called with c.mu held.
+func (c *common) decorate(s string, skip int) string {
+ frame := c.frameSkip(skip)
+ file := frame.File
+ line := frame.Line
+ if file != "" {
+ if *fullPath {
+ // If relative path, truncate file name at last file name separator.
+ } else if index := strings.LastIndexAny(file, `/\`); index >= 0 {
+ file = file[index+1:]
+ }
+ } else {
+ file = "???"
+ }
+ if line == 0 {
+ line = 1
+ }
+ buf := new(strings.Builder)
+ // Every line is indented at least 4 spaces.
+ buf.WriteString(" ")
+ fmt.Fprintf(buf, "%s:%d: ", file, line)
+ lines := strings.Split(s, "\n")
+ if l := len(lines); l > 1 && lines[l-1] == "" {
+ lines = lines[:l-1]
+ }
+ for i, line := range lines {
+ if i > 0 {
+ // Second and subsequent lines are indented an additional 4 spaces.
+ buf.WriteString("\n ")
+ }
+ buf.WriteString(line)
+ }
+ buf.WriteByte('\n')
+ return buf.String()
+}
+
+// flushToParent writes c.output to the parent after first writing the header
+// with the given format and arguments.
+func (c *common) flushToParent(testName, format string, args ...any) {
+ p := c.parent
+ p.mu.Lock()
+ defer p.mu.Unlock()
+
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ if len(c.output) > 0 {
+ // Add the current c.output to the print,
+ // and then arrange for the print to replace c.output.
+ // (This displays the logged output after the --- FAIL line.)
+ format += "%s"
+ args = append(args[:len(args):len(args)], c.output)
+ c.output = c.output[:0]
+ }
+
+ if c.chatty != nil && (p.w == c.chatty.w || c.chatty.json) {
+ // We're flushing to the actual output, so track that this output is
+ // associated with a specific test (and, specifically, that the next output
+ // is *not* associated with that test).
+ //
+ // Moreover, if c.output is non-empty it is important that this write be
+ // atomic with respect to the output of other tests, so that we don't end up
+ // with confusing '=== NAME' lines in the middle of our '--- PASS' block.
+ // Neither humans nor cmd/test2json can parse those easily.
+ // (See https://go.dev/issue/40771.)
+ //
+ // If test2json is used, we never flush to parent tests,
+ // so that the json stream shows subtests as they finish.
+ // (See https://go.dev/issue/29811.)
+ c.chatty.Updatef(testName, format, args...)
+ } else {
+ // We're flushing to the output buffer of the parent test, which will
+ // itself follow a test-name header when it is finally flushed to stdout.
+ fmt.Fprintf(p.w, c.chatty.prefix()+format, args...)
+ }
+}
+
+type indenter struct {
+ c *common
+}
+
+func (w indenter) Write(b []byte) (n int, err error) {
+ n = len(b)
+ for len(b) > 0 {
+ end := bytes.IndexByte(b, '\n')
+ if end == -1 {
+ end = len(b)
+ } else {
+ end++
+ }
+ // An indent of 4 spaces will neatly align the dashes with the status
+ // indicator of the parent.
+ line := b[:end]
+ if line[0] == marker {
+ w.c.output = append(w.c.output, marker)
+ line = line[1:]
+ }
+ const indent = " "
+ w.c.output = append(w.c.output, indent...)
+ w.c.output = append(w.c.output, line...)
+ b = b[end:]
+ }
+ return
+}
+
+// fmtDuration returns a string representing d in the form "87.00s".
+func fmtDuration(d time.Duration) string {
+ return fmt.Sprintf("%.2fs", d.Seconds())
+}
+
+// TB is the interface common to T, B, and F.
+type TB interface {
+ Cleanup(func())
+ Error(args ...any)
+ Errorf(format string, args ...any)
+ Fail()
+ FailNow()
+ Failed() bool
+ Fatal(args ...any)
+ Fatalf(format string, args ...any)
+ Helper()
+ Log(args ...any)
+ Logf(format string, args ...any)
+ Name() string
+ Setenv(key, value string)
+ Skip(args ...any)
+ SkipNow()
+ Skipf(format string, args ...any)
+ Skipped() bool
+ TempDir() string
+
+ // A private method to prevent users implementing the
+ // interface and so future additions to it will not
+ // violate Go 1 compatibility.
+ private()
+}
+
+var _ TB = (*T)(nil)
+var _ TB = (*B)(nil)
+
+// T is a type passed to Test functions to manage test state and support formatted test logs.
+//
+// A test ends when its Test function returns or calls any of the methods
+// FailNow, Fatal, Fatalf, SkipNow, Skip, or Skipf. Those methods, as well as
+// the Parallel method, must be called only from the goroutine running the
+// Test function.
+//
+// The other reporting methods, such as the variations of Log and Error,
+// may be called simultaneously from multiple goroutines.
+type T struct {
+ common
+ isEnvSet bool
+ context *testContext // For running tests and subtests.
+}
+
+func (c *common) private() {}
+
+// Name returns the name of the running (sub-) test or benchmark.
+//
+// The name will include the name of the test along with the names of
+// any nested sub-tests. If two sibling sub-tests have the same name,
+// Name will append a suffix to guarantee the returned name is unique.
+func (c *common) Name() string {
+ return c.name
+}
+
+func (c *common) setRan() {
+ if c.parent != nil {
+ c.parent.setRan()
+ }
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ c.ran = true
+}
+
+// Fail marks the function as having failed but continues execution.
+func (c *common) Fail() {
+ if c.parent != nil {
+ c.parent.Fail()
+ }
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ // c.done needs to be locked to synchronize checks to c.done in parent tests.
+ if c.done {
+ panic("Fail in goroutine after " + c.name + " has completed")
+ }
+ c.failed = true
+}
+
+// Failed reports whether the function has failed.
+func (c *common) Failed() bool {
+ c.mu.RLock()
+ defer c.mu.RUnlock()
+
+ if !c.done && int64(race.Errors()) > c.lastRaceErrors.Load() {
+ c.mu.RUnlock()
+ c.checkRaces()
+ c.mu.RLock()
+ }
+
+ return c.failed
+}
+
+// FailNow marks the function as having failed and stops its execution
+// by calling runtime.Goexit (which then runs all deferred calls in the
+// current goroutine).
+// Execution will continue at the next test or benchmark.
+// FailNow must be called from the goroutine running the
+// test or benchmark function, not from other goroutines
+// created during the test. Calling FailNow does not stop
+// those other goroutines.
+func (c *common) FailNow() {
+ c.checkFuzzFn("FailNow")
+ c.Fail()
+
+ // Calling runtime.Goexit will exit the goroutine, which
+ // will run the deferred functions in this goroutine,
+ // which will eventually run the deferred lines in tRunner,
+ // which will signal to the test loop that this test is done.
+ //
+ // A previous version of this code said:
+ //
+ // c.duration = ...
+ // c.signal <- c.self
+ // runtime.Goexit()
+ //
+ // This previous version duplicated code (those lines are in
+ // tRunner no matter what), but worse the goroutine teardown
+ // implicit in runtime.Goexit was not guaranteed to complete
+ // before the test exited. If a test deferred an important cleanup
+ // function (like removing temporary files), there was no guarantee
+ // it would run on a test failure. Because we send on c.signal during
+ // a top-of-stack deferred function now, we know that the send
+ // only happens after any other stacked defers have completed.
+ c.mu.Lock()
+ c.finished = true
+ c.mu.Unlock()
+ runtime.Goexit()
+}
+
+// log generates the output. It's always at the same stack depth.
+func (c *common) log(s string) {
+ c.logDepth(s, 3) // logDepth + log + public function
+}
+
+// logDepth generates the output at an arbitrary stack depth.
+func (c *common) logDepth(s string, depth int) {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ if c.done {
+ // This test has already finished. Try and log this message
+ // with our parent. If we don't have a parent, panic.
+ for parent := c.parent; parent != nil; parent = parent.parent {
+ parent.mu.Lock()
+ defer parent.mu.Unlock()
+ if !parent.done {
+ parent.output = append(parent.output, parent.decorate(s, depth+1)...)
+ return
+ }
+ }
+ panic("Log in goroutine after " + c.name + " has completed: " + s)
+ } else {
+ if c.chatty != nil {
+ if c.bench {
+ // Benchmarks don't print === CONT, so we should skip the test
+ // printer and just print straight to stdout.
+ fmt.Print(c.decorate(s, depth+1))
+ } else {
+ c.chatty.Printf(c.name, "%s", c.decorate(s, depth+1))
+ }
+
+ return
+ }
+ c.output = append(c.output, c.decorate(s, depth+1)...)
+ }
+}
+
+// Log formats its arguments using default formatting, analogous to Println,
+// and records the text in the error log. For tests, the text will be printed only if
+// the test fails or the -test.v flag is set. For benchmarks, the text is always
+// printed to avoid having performance depend on the value of the -test.v flag.
+func (c *common) Log(args ...any) {
+ c.checkFuzzFn("Log")
+ c.log(fmt.Sprintln(args...))
+}
+
+// Logf formats its arguments according to the format, analogous to Printf, and
+// records the text in the error log. A final newline is added if not provided. For
+// tests, the text will be printed only if the test fails or the -test.v flag is
+// set. For benchmarks, the text is always printed to avoid having performance
+// depend on the value of the -test.v flag.
+func (c *common) Logf(format string, args ...any) {
+ c.checkFuzzFn("Logf")
+ c.log(fmt.Sprintf(format, args...))
+}
+
+// Error is equivalent to Log followed by Fail.
+func (c *common) Error(args ...any) {
+ c.checkFuzzFn("Error")
+ c.log(fmt.Sprintln(args...))
+ c.Fail()
+}
+
+// Errorf is equivalent to Logf followed by Fail.
+func (c *common) Errorf(format string, args ...any) {
+ c.checkFuzzFn("Errorf")
+ c.log(fmt.Sprintf(format, args...))
+ c.Fail()
+}
+
+// Fatal is equivalent to Log followed by FailNow.
+func (c *common) Fatal(args ...any) {
+ c.checkFuzzFn("Fatal")
+ c.log(fmt.Sprintln(args...))
+ c.FailNow()
+}
+
+// Fatalf is equivalent to Logf followed by FailNow.
+func (c *common) Fatalf(format string, args ...any) {
+ c.checkFuzzFn("Fatalf")
+ c.log(fmt.Sprintf(format, args...))
+ c.FailNow()
+}
+
+// Skip is equivalent to Log followed by SkipNow.
+func (c *common) Skip(args ...any) {
+ c.checkFuzzFn("Skip")
+ c.log(fmt.Sprintln(args...))
+ c.SkipNow()
+}
+
+// Skipf is equivalent to Logf followed by SkipNow.
+func (c *common) Skipf(format string, args ...any) {
+ c.checkFuzzFn("Skipf")
+ c.log(fmt.Sprintf(format, args...))
+ c.SkipNow()
+}
+
+// SkipNow marks the test as having been skipped and stops its execution
+// by calling [runtime.Goexit].
+// If a test fails (see Error, Errorf, Fail) and is then skipped,
+// it is still considered to have failed.
+// Execution will continue at the next test or benchmark. See also FailNow.
+// SkipNow must be called from the goroutine running the test, not from
+// other goroutines created during the test. Calling SkipNow does not stop
+// those other goroutines.
+func (c *common) SkipNow() {
+ c.checkFuzzFn("SkipNow")
+ c.mu.Lock()
+ c.skipped = true
+ c.finished = true
+ c.mu.Unlock()
+ runtime.Goexit()
+}
+
+// Skipped reports whether the test was skipped.
+func (c *common) Skipped() bool {
+ c.mu.RLock()
+ defer c.mu.RUnlock()
+ return c.skipped
+}
+
+// Helper marks the calling function as a test helper function.
+// When printing file and line information, that function will be skipped.
+// Helper may be called simultaneously from multiple goroutines.
+func (c *common) Helper() {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ if c.helperPCs == nil {
+ c.helperPCs = make(map[uintptr]struct{})
+ }
+ // repeating code from callerName here to save walking a stack frame
+ var pc [1]uintptr
+ n := runtime.Callers(2, pc[:]) // skip runtime.Callers + Helper
+ if n == 0 {
+ panic("testing: zero callers found")
+ }
+ if _, found := c.helperPCs[pc[0]]; !found {
+ c.helperPCs[pc[0]] = struct{}{}
+ c.helperNames = nil // map will be recreated next time it is needed
+ }
+}
+
+// Cleanup registers a function to be called when the test (or subtest) and all its
+// subtests complete. Cleanup functions will be called in last added,
+// first called order.
+func (c *common) Cleanup(f func()) {
+ c.checkFuzzFn("Cleanup")
+ var pc [maxStackLen]uintptr
+ // Skip two extra frames to account for this function and runtime.Callers itself.
+ n := runtime.Callers(2, pc[:])
+ cleanupPc := pc[:n]
+
+ fn := func() {
+ defer func() {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ c.cleanupName = ""
+ c.cleanupPc = nil
+ }()
+
+ name := callerName(0)
+ c.mu.Lock()
+ c.cleanupName = name
+ c.cleanupPc = cleanupPc
+ c.mu.Unlock()
+
+ f()
+ }
+
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ c.cleanups = append(c.cleanups, fn)
+}
+
+// TempDir returns a temporary directory for the test to use.
+// The directory is automatically removed when the test and
+// all its subtests complete.
+// Each subsequent call to t.TempDir returns a unique directory;
+// if the directory creation fails, TempDir terminates the test by calling Fatal.
+func (c *common) TempDir() string {
+ c.checkFuzzFn("TempDir")
+ // Use a single parent directory for all the temporary directories
+ // created by a test, each numbered sequentially.
+ c.tempDirMu.Lock()
+ var nonExistent bool
+ if c.tempDir == "" { // Usually the case with js/wasm
+ nonExistent = true
+ } else {
+ _, err := os.Stat(c.tempDir)
+ nonExistent = os.IsNotExist(err)
+ if err != nil && !nonExistent {
+ c.Fatalf("TempDir: %v", err)
+ }
+ }
+
+ if nonExistent {
+ c.Helper()
+
+ // Drop unusual characters (such as path separators or
+ // characters interacting with globs) from the directory name to
+ // avoid surprising os.MkdirTemp behavior.
+ mapper := func(r rune) rune {
+ if r < utf8.RuneSelf {
+ const allowed = "!#$%&()+,-.=@^_{}~ "
+ if '0' <= r && r <= '9' ||
+ 'a' <= r && r <= 'z' ||
+ 'A' <= r && r <= 'Z' {
+ return r
+ }
+ if strings.ContainsRune(allowed, r) {
+ return r
+ }
+ } else if unicode.IsLetter(r) || unicode.IsNumber(r) {
+ return r
+ }
+ return -1
+ }
+ pattern := strings.Map(mapper, c.Name())
+ c.tempDir, c.tempDirErr = os.MkdirTemp("", pattern)
+ if c.tempDirErr == nil {
+ c.Cleanup(func() {
+ if err := removeAll(c.tempDir); err != nil {
+ c.Errorf("TempDir RemoveAll cleanup: %v", err)
+ }
+ })
+ }
+ }
+
+ if c.tempDirErr == nil {
+ c.tempDirSeq++
+ }
+ seq := c.tempDirSeq
+ c.tempDirMu.Unlock()
+
+ if c.tempDirErr != nil {
+ c.Fatalf("TempDir: %v", c.tempDirErr)
+ }
+
+ dir := fmt.Sprintf("%s%c%03d", c.tempDir, os.PathSeparator, seq)
+ if err := os.Mkdir(dir, 0777); err != nil {
+ c.Fatalf("TempDir: %v", err)
+ }
+ return dir
+}
+
+// removeAll is like os.RemoveAll, but retries Windows "Access is denied."
+// errors up to an arbitrary timeout.
+//
+// Those errors have been known to occur spuriously on at least the
+// windows-amd64-2012 builder (https://go.dev/issue/50051), and can only occur
+// legitimately if the test leaves behind a temp file that either is still open
+// or the test otherwise lacks permission to delete. In the case of legitimate
+// failures, a failing test may take a bit longer to fail, but once the test is
+// fixed the extra latency will go away.
+func removeAll(path string) error {
+ const arbitraryTimeout = 2 * time.Second
+ var (
+ start time.Time
+ nextSleep = 1 * time.Millisecond
+ )
+ for {
+ err := os.RemoveAll(path)
+ if !isWindowsRetryable(err) {
+ return err
+ }
+ if start.IsZero() {
+ start = time.Now()
+ } else if d := time.Since(start) + nextSleep; d >= arbitraryTimeout {
+ return err
+ }
+ time.Sleep(nextSleep)
+ nextSleep += time.Duration(rand.Int63n(int64(nextSleep)))
+ }
+}
+
+// Setenv calls os.Setenv(key, value) and uses Cleanup to
+// restore the environment variable to its original value
+// after the test.
+//
+// Because Setenv affects the whole process, it cannot be used
+// in parallel tests or tests with parallel ancestors.
+func (c *common) Setenv(key, value string) {
+ c.checkFuzzFn("Setenv")
+ prevValue, ok := os.LookupEnv(key)
+
+ if err := os.Setenv(key, value); err != nil {
+ c.Fatalf("cannot set environment variable: %v", err)
+ }
+
+ if ok {
+ c.Cleanup(func() {
+ os.Setenv(key, prevValue)
+ })
+ } else {
+ c.Cleanup(func() {
+ os.Unsetenv(key)
+ })
+ }
+}
+
+// panicHanding controls the panic handling used by runCleanup.
+type panicHandling int
+
+const (
+ normalPanic panicHandling = iota
+ recoverAndReturnPanic
+)
+
+// runCleanup is called at the end of the test.
+// If ph is recoverAndReturnPanic, it will catch panics, and return the
+// recovered value if any.
+func (c *common) runCleanup(ph panicHandling) (panicVal any) {
+ c.cleanupStarted.Store(true)
+ defer c.cleanupStarted.Store(false)
+
+ if ph == recoverAndReturnPanic {
+ defer func() {
+ panicVal = recover()
+ }()
+ }
+
+ // Make sure that if a cleanup function panics,
+ // we still run the remaining cleanup functions.
+ defer func() {
+ c.mu.Lock()
+ recur := len(c.cleanups) > 0
+ c.mu.Unlock()
+ if recur {
+ c.runCleanup(normalPanic)
+ }
+ }()
+
+ for {
+ var cleanup func()
+ c.mu.Lock()
+ if len(c.cleanups) > 0 {
+ last := len(c.cleanups) - 1
+ cleanup = c.cleanups[last]
+ c.cleanups = c.cleanups[:last]
+ }
+ c.mu.Unlock()
+ if cleanup == nil {
+ return nil
+ }
+ cleanup()
+ }
+}
+
+// resetRaces updates c.parent's count of data race errors (or the global count,
+// if c has no parent), and updates c.lastRaceErrors to match.
+//
+// Any races that occurred prior to this call to resetRaces will
+// not be attributed to c.
+func (c *common) resetRaces() {
+ if c.parent == nil {
+ c.lastRaceErrors.Store(int64(race.Errors()))
+ } else {
+ c.lastRaceErrors.Store(c.parent.checkRaces())
+ }
+}
+
+// checkRaces checks whether the global count of data race errors has increased
+// since c's count was last reset.
+//
+// If so, it marks c as having failed due to those races (logging an error for
+// the first such race), and updates the race counts for the parents of c so
+// that if they are currently suspended (such as in a call to T.Run) they will
+// not log separate errors for the race(s).
+//
+// Note that multiple tests may be marked as failed due to the same race if they
+// are executing in parallel.
+func (c *common) checkRaces() (raceErrors int64) {
+ raceErrors = int64(race.Errors())
+ for {
+ last := c.lastRaceErrors.Load()
+ if raceErrors <= last {
+ // All races have already been reported.
+ return raceErrors
+ }
+ if c.lastRaceErrors.CompareAndSwap(last, raceErrors) {
+ break
+ }
+ }
+
+ if c.raceErrorLogged.CompareAndSwap(false, true) {
+ // This is the first race we've encountered for this test.
+ // Mark the test as failed, and log the reason why only once.
+ // (Note that the race detector itself will still write a goroutine
+ // dump for any further races it detects.)
+ c.Errorf("race detected during execution of test")
+ }
+
+ // Update the parent(s) of this test so that they don't re-report the race.
+ parent := c.parent
+ for parent != nil {
+ for {
+ last := parent.lastRaceErrors.Load()
+ if raceErrors <= last {
+ // This race was already reported by another (likely parallel) subtest.
+ return raceErrors
+ }
+ if parent.lastRaceErrors.CompareAndSwap(last, raceErrors) {
+ break
+ }
+ }
+ parent = parent.parent
+ }
+
+ return raceErrors
+}
+
+// callerName gives the function name (qualified with a package path)
+// for the caller after skip frames (where 0 means the current function).
+func callerName(skip int) string {
+ var pc [1]uintptr
+ n := runtime.Callers(skip+2, pc[:]) // skip + runtime.Callers + callerName
+ if n == 0 {
+ panic("testing: zero callers found")
+ }
+ return pcToName(pc[0])
+}
+
+func pcToName(pc uintptr) string {
+ pcs := []uintptr{pc}
+ frames := runtime.CallersFrames(pcs)
+ frame, _ := frames.Next()
+ return frame.Function
+}
+
+// Parallel signals that this test is to be run in parallel with (and only with)
+// other parallel tests. When a test is run multiple times due to use of
+// -test.count or -test.cpu, multiple instances of a single test never run in
+// parallel with each other.
+func (t *T) Parallel() {
+ if t.isParallel {
+ panic("testing: t.Parallel called multiple times")
+ }
+ if t.isEnvSet {
+ panic("testing: t.Parallel called after t.Setenv; cannot set environment variables in parallel tests")
+ }
+ t.isParallel = true
+ if t.parent.barrier == nil {
+ // T.Parallel has no effect when fuzzing.
+ // Multiple processes may run in parallel, but only one input can run at a
+ // time per process so we can attribute crashes to specific inputs.
+ return
+ }
+
+ // We don't want to include the time we spend waiting for serial tests
+ // in the test duration. Record the elapsed time thus far and reset the
+ // timer afterwards.
+ t.duration += time.Since(t.start)
+
+ // Add to the list of tests to be released by the parent.
+ t.parent.sub = append(t.parent.sub, t)
+
+ // Report any races during execution of this test up to this point.
+ //
+ // We will assume that any races that occur between here and the point where
+ // we unblock are not caused by this subtest. That assumption usually holds,
+ // although it can be wrong if the test spawns a goroutine that races in the
+ // background while the rest of the test is blocked on the call to Parallel.
+ // If that happens, we will misattribute the background race to some other
+ // test, or to no test at all — but that false-negative is so unlikely that it
+ // is not worth adding race-report noise for the common case where the test is
+ // completely suspended during the call to Parallel.
+ t.checkRaces()
+
+ if t.chatty != nil {
+ t.chatty.Updatef(t.name, "=== PAUSE %s\n", t.name)
+ }
+ running.Delete(t.name)
+
+ t.signal <- true // Release calling test.
+ <-t.parent.barrier // Wait for the parent test to complete.
+ t.context.waitParallel()
+
+ if t.chatty != nil {
+ t.chatty.Updatef(t.name, "=== CONT %s\n", t.name)
+ }
+ running.Store(t.name, time.Now())
+ t.start = time.Now()
+
+ // Reset the local race counter to ignore any races that happened while this
+ // goroutine was blocked, such as in the parent test or in other parallel
+ // subtests.
+ //
+ // (Note that we don't call parent.checkRaces here:
+ // if other parallel subtests have already introduced races, we want to
+ // let them report those races instead of attributing them to the parent.)
+ t.lastRaceErrors.Store(int64(race.Errors()))
+}
+
+// Setenv calls os.Setenv(key, value) and uses Cleanup to
+// restore the environment variable to its original value
+// after the test.
+//
+// Because Setenv affects the whole process, it cannot be used
+// in parallel tests or tests with parallel ancestors.
+func (t *T) Setenv(key, value string) {
+ // Non-parallel subtests that have parallel ancestors may still
+ // run in parallel with other tests: they are only non-parallel
+ // with respect to the other subtests of the same parent.
+ // Since SetEnv affects the whole process, we need to disallow it
+ // if the current test or any parent is parallel.
+ isParallel := false
+ for c := &t.common; c != nil; c = c.parent {
+ if c.isParallel {
+ isParallel = true
+ break
+ }
+ }
+ if isParallel {
+ panic("testing: t.Setenv called after t.Parallel; cannot set environment variables in parallel tests")
+ }
+
+ t.isEnvSet = true
+
+ t.common.Setenv(key, value)
+}
+
+// InternalTest is an internal type but exported because it is cross-package;
+// it is part of the implementation of the "go test" command.
+type InternalTest struct {
+ Name string
+ F func(*T)
+}
+
+var errNilPanicOrGoexit = errors.New("test executed panic(nil) or runtime.Goexit")
+
+func tRunner(t *T, fn func(t *T)) {
+ t.runner = callerName(0)
+
+ // When this goroutine is done, either because fn(t)
+ // returned normally or because a test failure triggered
+ // a call to runtime.Goexit, record the duration and send
+ // a signal saying that the test is done.
+ defer func() {
+ t.checkRaces()
+
+ // TODO(#61034): This is the wrong place for this check.
+ if t.Failed() {
+ numFailed.Add(1)
+ }
+
+ // Check if the test panicked or Goexited inappropriately.
+ //
+ // If this happens in a normal test, print output but continue panicking.
+ // tRunner is called in its own goroutine, so this terminates the process.
+ //
+ // If this happens while fuzzing, recover from the panic and treat it like a
+ // normal failure. It's important that the process keeps running in order to
+ // find short inputs that cause panics.
+ err := recover()
+ signal := true
+
+ t.mu.RLock()
+ finished := t.finished
+ t.mu.RUnlock()
+ if !finished && err == nil {
+ err = errNilPanicOrGoexit
+ for p := t.parent; p != nil; p = p.parent {
+ p.mu.RLock()
+ finished = p.finished
+ p.mu.RUnlock()
+ if finished {
+ if !t.isParallel {
+ t.Errorf("%v: subtest may have called FailNow on a parent test", err)
+ err = nil
+ }
+ signal = false
+ break
+ }
+ }
+ }
+
+ if err != nil && t.context.isFuzzing {
+ prefix := "panic: "
+ if err == errNilPanicOrGoexit {
+ prefix = ""
+ }
+ t.Errorf("%s%s\n%s\n", prefix, err, string(debug.Stack()))
+ t.mu.Lock()
+ t.finished = true
+ t.mu.Unlock()
+ err = nil
+ }
+
+ // Use a deferred call to ensure that we report that the test is
+ // complete even if a cleanup function calls t.FailNow. See issue 41355.
+ didPanic := false
+ defer func() {
+ // Only report that the test is complete if it doesn't panic,
+ // as otherwise the test binary can exit before the panic is
+ // reported to the user. See issue 41479.
+ if didPanic {
+ return
+ }
+ if err != nil {
+ panic(err)
+ }
+ running.Delete(t.name)
+ t.signal <- signal
+ }()
+
+ doPanic := func(err any) {
+ t.Fail()
+ if r := t.runCleanup(recoverAndReturnPanic); r != nil {
+ t.Logf("cleanup panicked with %v", r)
+ }
+ // Flush the output log up to the root before dying.
+ for root := &t.common; root.parent != nil; root = root.parent {
+ root.mu.Lock()
+ root.duration += time.Since(root.start)
+ d := root.duration
+ root.mu.Unlock()
+ root.flushToParent(root.name, "--- FAIL: %s (%s)\n", root.name, fmtDuration(d))
+ if r := root.parent.runCleanup(recoverAndReturnPanic); r != nil {
+ fmt.Fprintf(root.parent.w, "cleanup panicked with %v", r)
+ }
+ }
+ didPanic = true
+ panic(err)
+ }
+ if err != nil {
+ doPanic(err)
+ }
+
+ t.duration += time.Since(t.start)
+
+ if len(t.sub) > 0 {
+ // Run parallel subtests.
+
+ // Decrease the running count for this test and mark it as no longer running.
+ t.context.release()
+ running.Delete(t.name)
+
+ // Release the parallel subtests.
+ close(t.barrier)
+ // Wait for subtests to complete.
+ for _, sub := range t.sub {
+ <-sub.signal
+ }
+
+ // Run any cleanup callbacks, marking the test as running
+ // in case the cleanup hangs.
+ cleanupStart := time.Now()
+ running.Store(t.name, cleanupStart)
+ err := t.runCleanup(recoverAndReturnPanic)
+ t.duration += time.Since(cleanupStart)
+ if err != nil {
+ doPanic(err)
+ }
+ t.checkRaces()
+ if !t.isParallel {
+ // Reacquire the count for sequential tests. See comment in Run.
+ t.context.waitParallel()
+ }
+ } else if t.isParallel {
+ // Only release the count for this test if it was run as a parallel
+ // test. See comment in Run method.
+ t.context.release()
+ }
+ t.report() // Report after all subtests have finished.
+
+ // Do not lock t.done to allow race detector to detect race in case
+ // the user does not appropriately synchronize a goroutine.
+ t.done = true
+ if t.parent != nil && !t.hasSub.Load() {
+ t.setRan()
+ }
+ }()
+ defer func() {
+ if len(t.sub) == 0 {
+ t.runCleanup(normalPanic)
+ }
+ }()
+
+ t.start = time.Now()
+ t.resetRaces()
+ fn(t)
+
+ // code beyond here will not be executed when FailNow is invoked
+ t.mu.Lock()
+ t.finished = true
+ t.mu.Unlock()
+}
+
+// Run runs f as a subtest of t called name. It runs f in a separate goroutine
+// and blocks until f returns or calls t.Parallel to become a parallel test.
+// Run reports whether f succeeded (or at least did not fail before calling t.Parallel).
+//
+// Run may be called simultaneously from multiple goroutines, but all such calls
+// must return before the outer test function for t returns.
+func (t *T) Run(name string, f func(t *T)) bool {
+ if t.cleanupStarted.Load() {
+ panic("testing: t.Run called during t.Cleanup")
+ }
+
+ t.hasSub.Store(true)
+ testName, ok, _ := t.context.match.fullName(&t.common, name)
+ if !ok || shouldFailFast() {
+ return true
+ }
+ // Record the stack trace at the point of this call so that if the subtest
+ // function - which runs in a separate stack - is marked as a helper, we can
+ // continue walking the stack into the parent test.
+ var pc [maxStackLen]uintptr
+ n := runtime.Callers(2, pc[:])
+ t = &T{
+ common: common{
+ barrier: make(chan bool),
+ signal: make(chan bool, 1),
+ name: testName,
+ parent: &t.common,
+ level: t.level + 1,
+ creator: pc[:n],
+ chatty: t.chatty,
+ },
+ context: t.context,
+ }
+ t.w = indenter{&t.common}
+
+ if t.chatty != nil {
+ t.chatty.Updatef(t.name, "=== RUN %s\n", t.name)
+ }
+ running.Store(t.name, time.Now())
+
+ // Instead of reducing the running count of this test before calling the
+ // tRunner and increasing it afterwards, we rely on tRunner keeping the
+ // count correct. This ensures that a sequence of sequential tests runs
+ // without being preempted, even when their parent is a parallel test. This
+ // may especially reduce surprises if *parallel == 1.
+ go tRunner(t, f)
+
+ // The parent goroutine will block until the subtest either finishes or calls
+ // Parallel, but in general we don't know whether the parent goroutine is the
+ // top-level test function or some other goroutine it has spawned.
+ // To avoid confusing false-negatives, we leave the parent in the running map
+ // even though in the typical case it is blocked.
+
+ if !<-t.signal {
+ // At this point, it is likely that FailNow was called on one of the
+ // parent tests by one of the subtests. Continue aborting up the chain.
+ runtime.Goexit()
+ }
+
+ if t.chatty != nil && t.chatty.json {
+ t.chatty.Updatef(t.parent.name, "=== NAME %s\n", t.parent.name)
+ }
+ return !t.failed
+}
+
+// Deadline reports the time at which the test binary will have
+// exceeded the timeout specified by the -timeout flag.
+//
+// The ok result is false if the -timeout flag indicates “no timeout” (0).
+func (t *T) Deadline() (deadline time.Time, ok bool) {
+ deadline = t.context.deadline
+ return deadline, !deadline.IsZero()
+}
+
+// testContext holds all fields that are common to all tests. This includes
+// synchronization primitives to run at most *parallel tests.
+type testContext struct {
+ match *matcher
+ deadline time.Time
+
+ // isFuzzing is true in the context used when generating random inputs
+ // for fuzz targets. isFuzzing is false when running normal tests and
+ // when running fuzz tests as unit tests (without -fuzz or when -fuzz
+ // does not match).
+ isFuzzing bool
+
+ mu sync.Mutex
+
+ // Channel used to signal tests that are ready to be run in parallel.
+ startParallel chan bool
+
+ // running is the number of tests currently running in parallel.
+ // This does not include tests that are waiting for subtests to complete.
+ running int
+
+ // numWaiting is the number tests waiting to be run in parallel.
+ numWaiting int
+
+ // maxParallel is a copy of the parallel flag.
+ maxParallel int
+}
+
+func newTestContext(maxParallel int, m *matcher) *testContext {
+ return &testContext{
+ match: m,
+ startParallel: make(chan bool),
+ maxParallel: maxParallel,
+ running: 1, // Set the count to 1 for the main (sequential) test.
+ }
+}
+
+func (c *testContext) waitParallel() {
+ c.mu.Lock()
+ if c.running < c.maxParallel {
+ c.running++
+ c.mu.Unlock()
+ return
+ }
+ c.numWaiting++
+ c.mu.Unlock()
+ <-c.startParallel
+}
+
+func (c *testContext) release() {
+ c.mu.Lock()
+ if c.numWaiting == 0 {
+ c.running--
+ c.mu.Unlock()
+ return
+ }
+ c.numWaiting--
+ c.mu.Unlock()
+ c.startParallel <- true // Pick a waiting test to be run.
+}
+
+// No one should be using func Main anymore.
+// See the doc comment on func Main and use MainStart instead.
+var errMain = errors.New("testing: unexpected use of func Main")
+
+type matchStringOnly func(pat, str string) (bool, error)
+
+func (f matchStringOnly) MatchString(pat, str string) (bool, error) { return f(pat, str) }
+func (f matchStringOnly) StartCPUProfile(w io.Writer) error { return errMain }
+func (f matchStringOnly) StopCPUProfile() {}
+func (f matchStringOnly) WriteProfileTo(string, io.Writer, int) error { return errMain }
+func (f matchStringOnly) ImportPath() string { return "" }
+func (f matchStringOnly) StartTestLog(io.Writer) {}
+func (f matchStringOnly) StopTestLog() error { return errMain }
+func (f matchStringOnly) SetPanicOnExit0(bool) {}
+func (f matchStringOnly) CoordinateFuzzing(time.Duration, int64, time.Duration, int64, int, []corpusEntry, []reflect.Type, string, string) error {
+ return errMain
+}
+func (f matchStringOnly) RunFuzzWorker(func(corpusEntry) error) error { return errMain }
+func (f matchStringOnly) ReadCorpus(string, []reflect.Type) ([]corpusEntry, error) {
+ return nil, errMain
+}
+func (f matchStringOnly) CheckCorpus([]any, []reflect.Type) error { return nil }
+func (f matchStringOnly) ResetCoverage() {}
+func (f matchStringOnly) SnapshotCoverage() {}
+
+// Main is an internal function, part of the implementation of the "go test" command.
+// It was exported because it is cross-package and predates "internal" packages.
+// It is no longer used by "go test" but preserved, as much as possible, for other
+// systems that simulate "go test" using Main, but Main sometimes cannot be updated as
+// new functionality is added to the testing package.
+// Systems simulating "go test" should be updated to use MainStart.
+func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) {
+ os.Exit(MainStart(matchStringOnly(matchString), tests, benchmarks, nil, examples).Run())
+}
+
+// M is a type passed to a TestMain function to run the actual tests.
+type M struct {
+ deps testDeps
+ tests []InternalTest
+ benchmarks []InternalBenchmark
+ fuzzTargets []InternalFuzzTarget
+ examples []InternalExample
+
+ timer *time.Timer
+ afterOnce sync.Once
+
+ numRun int
+
+ // value to pass to os.Exit, the outer test func main
+ // harness calls os.Exit with this code. See #34129.
+ exitCode int
+}
+
+// testDeps is an internal interface of functionality that is
+// passed into this package by a test's generated main package.
+// The canonical implementation of this interface is
+// testing/internal/testdeps's TestDeps.
+type testDeps interface {
+ ImportPath() string
+ MatchString(pat, str string) (bool, error)
+ SetPanicOnExit0(bool)
+ StartCPUProfile(io.Writer) error
+ StopCPUProfile()
+ StartTestLog(io.Writer)
+ StopTestLog() error
+ WriteProfileTo(string, io.Writer, int) error
+ CoordinateFuzzing(time.Duration, int64, time.Duration, int64, int, []corpusEntry, []reflect.Type, string, string) error
+ RunFuzzWorker(func(corpusEntry) error) error
+ ReadCorpus(string, []reflect.Type) ([]corpusEntry, error)
+ CheckCorpus([]any, []reflect.Type) error
+ ResetCoverage()
+ SnapshotCoverage()
+}
+
+// MainStart is meant for use by tests generated by 'go test'.
+// It is not meant to be called directly and is not subject to the Go 1 compatibility document.
+// It may change signature from release to release.
+func MainStart(deps testDeps, tests []InternalTest, benchmarks []InternalBenchmark, fuzzTargets []InternalFuzzTarget, examples []InternalExample) *M {
+ Init()
+ return &M{
+ deps: deps,
+ tests: tests,
+ benchmarks: benchmarks,
+ fuzzTargets: fuzzTargets,
+ examples: examples,
+ }
+}
+
+var testingTesting bool
+var realStderr *os.File
+
+// Run runs the tests. It returns an exit code to pass to os.Exit.
+func (m *M) Run() (code int) {
+ defer func() {
+ code = m.exitCode
+ }()
+
+ // Count the number of calls to m.Run.
+ // We only ever expected 1, but we didn't enforce that,
+ // and now there are tests in the wild that call m.Run multiple times.
+ // Sigh. go.dev/issue/23129.
+ m.numRun++
+
+ // TestMain may have already called flag.Parse.
+ if !flag.Parsed() {
+ flag.Parse()
+ }
+
+ if chatty.json {
+ // With -v=json, stdout and stderr are pointing to the same pipe,
+ // which is leading into test2json. In general, operating systems
+ // do a good job of ensuring that writes to the same pipe through
+ // different file descriptors are delivered whole, so that writing
+ // AAA to stdout and BBB to stderr simultaneously produces
+ // AAABBB or BBBAAA on the pipe, not something like AABBBA.
+ // However, the exception to this is when the pipe fills: in that
+ // case, Go's use of non-blocking I/O means that writing AAA
+ // or BBB might be split across multiple system calls, making it
+ // entirely possible to get output like AABBBA. The same problem
+ // happens inside the operating system kernel if we switch to
+ // blocking I/O on the pipe. This interleaved output can do things
+ // like print unrelated messages in the middle of a TestFoo line,
+ // which confuses test2json. Setting os.Stderr = os.Stdout will make
+ // them share a single pfd, which will hold a lock for each program
+ // write, preventing any interleaving.
+ //
+ // It might be nice to set Stderr = Stdout always, or perhaps if
+ // we can tell they are the same file, but for now -v=json is
+ // a very clear signal. Making the two files the same may cause
+ // surprises if programs close os.Stdout but expect to be able
+ // to continue to write to os.Stderr, but it's hard to see why a
+ // test would think it could take over global state that way.
+ //
+ // This fix only helps programs where the output is coming directly
+ // from Go code. It does not help programs in which a subprocess is
+ // writing to stderr or stdout at the same time that a Go test is writing output.
+ // It also does not help when the output is coming from the runtime,
+ // such as when using the print/println functions, since that code writes
+ // directly to fd 2 without any locking.
+ // We keep realStderr around to prevent fd 2 from being closed.
+ //
+ // See go.dev/issue/33419.
+ realStderr = os.Stderr
+ os.Stderr = os.Stdout
+ }
+
+ if *parallel < 1 {
+ fmt.Fprintln(os.Stderr, "testing: -parallel can only be given a positive integer")
+ flag.Usage()
+ m.exitCode = 2
+ return
+ }
+ if *matchFuzz != "" && *fuzzCacheDir == "" {
+ fmt.Fprintln(os.Stderr, "testing: -test.fuzzcachedir must be set if -test.fuzz is set")
+ flag.Usage()
+ m.exitCode = 2
+ return
+ }
+
+ if *matchList != "" {
+ listTests(m.deps.MatchString, m.tests, m.benchmarks, m.fuzzTargets, m.examples)
+ m.exitCode = 0
+ return
+ }
+
+ if *shuffle != "off" {
+ var n int64
+ var err error
+ if *shuffle == "on" {
+ n = time.Now().UnixNano()
+ } else {
+ n, err = strconv.ParseInt(*shuffle, 10, 64)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, `testing: -shuffle should be "off", "on", or a valid integer:`, err)
+ m.exitCode = 2
+ return
+ }
+ }
+ fmt.Println("-test.shuffle", n)
+ rng := rand.New(rand.NewSource(n))
+ rng.Shuffle(len(m.tests), func(i, j int) { m.tests[i], m.tests[j] = m.tests[j], m.tests[i] })
+ rng.Shuffle(len(m.benchmarks), func(i, j int) { m.benchmarks[i], m.benchmarks[j] = m.benchmarks[j], m.benchmarks[i] })
+ }
+
+ parseCpuList()
+
+ m.before()
+ defer m.after()
+
+ // Run tests, examples, and benchmarks unless this is a fuzz worker process.
+ // Workers start after this is done by their parent process, and they should
+ // not repeat this work.
+ if !*isFuzzWorker {
+ deadline := m.startAlarm()
+ haveExamples = len(m.examples) > 0
+ testRan, testOk := runTests(m.deps.MatchString, m.tests, deadline)
+ fuzzTargetsRan, fuzzTargetsOk := runFuzzTests(m.deps, m.fuzzTargets, deadline)
+ exampleRan, exampleOk := runExamples(m.deps.MatchString, m.examples)
+ m.stopAlarm()
+ if !testRan && !exampleRan && !fuzzTargetsRan && *matchBenchmarks == "" && *matchFuzz == "" {
+ fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
+ if testingTesting && *match != "^$" {
+ // If this happens during testing of package testing it could be that
+ // package testing's own logic for when to run a test is broken,
+ // in which case every test will run nothing and succeed,
+ // with no obvious way to detect this problem (since no tests are running).
+ // So make 'no tests to run' a hard failure when testing package testing itself.
+ fmt.Print(chatty.prefix(), "FAIL: package testing must run tests\n")
+ testOk = false
+ }
+ }
+ anyFailed := !testOk || !exampleOk || !fuzzTargetsOk || !runBenchmarks(m.deps.ImportPath(), m.deps.MatchString, m.benchmarks)
+ if !anyFailed && race.Errors() > 0 {
+ fmt.Print(chatty.prefix(), "testing: race detected outside of test execution\n")
+ anyFailed = true
+ }
+ if anyFailed {
+ fmt.Print(chatty.prefix(), "FAIL\n")
+ m.exitCode = 1
+ return
+ }
+ }
+
+ fuzzingOk := runFuzzing(m.deps, m.fuzzTargets)
+ if !fuzzingOk {
+ fmt.Print(chatty.prefix(), "FAIL\n")
+ if *isFuzzWorker {
+ m.exitCode = fuzzWorkerExitCode
+ } else {
+ m.exitCode = 1
+ }
+ return
+ }
+
+ m.exitCode = 0
+ if !*isFuzzWorker {
+ fmt.Print(chatty.prefix(), "PASS\n")
+ }
+ return
+}
+
+func (t *T) report() {
+ if t.parent == nil {
+ return
+ }
+ dstr := fmtDuration(t.duration)
+ format := "--- %s: %s (%s)\n"
+ if t.Failed() {
+ t.flushToParent(t.name, format, "FAIL", t.name, dstr)
+ } else if t.chatty != nil {
+ if t.Skipped() {
+ t.flushToParent(t.name, format, "SKIP", t.name, dstr)
+ } else {
+ t.flushToParent(t.name, format, "PASS", t.name, dstr)
+ }
+ }
+}
+
+func listTests(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, fuzzTargets []InternalFuzzTarget, examples []InternalExample) {
+ if _, err := matchString(*matchList, "non-empty"); err != nil {
+ fmt.Fprintf(os.Stderr, "testing: invalid regexp in -test.list (%q): %s\n", *matchList, err)
+ os.Exit(1)
+ }
+
+ for _, test := range tests {
+ if ok, _ := matchString(*matchList, test.Name); ok {
+ fmt.Println(test.Name)
+ }
+ }
+ for _, bench := range benchmarks {
+ if ok, _ := matchString(*matchList, bench.Name); ok {
+ fmt.Println(bench.Name)
+ }
+ }
+ for _, fuzzTarget := range fuzzTargets {
+ if ok, _ := matchString(*matchList, fuzzTarget.Name); ok {
+ fmt.Println(fuzzTarget.Name)
+ }
+ }
+ for _, example := range examples {
+ if ok, _ := matchString(*matchList, example.Name); ok {
+ fmt.Println(example.Name)
+ }
+ }
+}
+
+// RunTests is an internal function but exported because it is cross-package;
+// it is part of the implementation of the "go test" command.
+func RunTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ok bool) {
+ var deadline time.Time
+ if *timeout > 0 {
+ deadline = time.Now().Add(*timeout)
+ }
+ ran, ok := runTests(matchString, tests, deadline)
+ if !ran && !haveExamples {
+ fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
+ }
+ return ok
+}
+
+func runTests(matchString func(pat, str string) (bool, error), tests []InternalTest, deadline time.Time) (ran, ok bool) {
+ ok = true
+ for _, procs := range cpuList {
+ runtime.GOMAXPROCS(procs)
+ for i := uint(0); i < *count; i++ {
+ if shouldFailFast() {
+ break
+ }
+ if i > 0 && !ran {
+ // There were no tests to run on the first
+ // iteration. This won't change, so no reason
+ // to keep trying.
+ break
+ }
+ ctx := newTestContext(*parallel, newMatcher(matchString, *match, "-test.run", *skip))
+ ctx.deadline = deadline
+ t := &T{
+ common: common{
+ signal: make(chan bool, 1),
+ barrier: make(chan bool),
+ w: os.Stdout,
+ },
+ context: ctx,
+ }
+ if Verbose() {
+ t.chatty = newChattyPrinter(t.w)
+ }
+ tRunner(t, func(t *T) {
+ for _, test := range tests {
+ t.Run(test.Name, test.F)
+ }
+ })
+ select {
+ case <-t.signal:
+ default:
+ panic("internal error: tRunner exited without sending on t.signal")
+ }
+ ok = ok && !t.Failed()
+ ran = ran || t.ran
+ }
+ }
+ return ran, ok
+}
+
+// before runs before all testing.
+func (m *M) before() {
+ if *memProfileRate > 0 {
+ runtime.MemProfileRate = *memProfileRate
+ }
+ if *cpuProfile != "" {
+ f, err := os.Create(toOutputDir(*cpuProfile))
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "testing: %s\n", err)
+ return
+ }
+ if err := m.deps.StartCPUProfile(f); err != nil {
+ fmt.Fprintf(os.Stderr, "testing: can't start cpu profile: %s\n", err)
+ f.Close()
+ return
+ }
+ // Could save f so after can call f.Close; not worth the effort.
+ }
+ if *traceFile != "" {
+ f, err := os.Create(toOutputDir(*traceFile))
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "testing: %s\n", err)
+ return
+ }
+ if err := trace.Start(f); err != nil {
+ fmt.Fprintf(os.Stderr, "testing: can't start tracing: %s\n", err)
+ f.Close()
+ return
+ }
+ // Could save f so after can call f.Close; not worth the effort.
+ }
+ if *blockProfile != "" && *blockProfileRate >= 0 {
+ runtime.SetBlockProfileRate(*blockProfileRate)
+ }
+ if *mutexProfile != "" && *mutexProfileFraction >= 0 {
+ runtime.SetMutexProfileFraction(*mutexProfileFraction)
+ }
+ if *coverProfile != "" && CoverMode() == "" {
+ fmt.Fprintf(os.Stderr, "testing: cannot use -test.coverprofile because test binary was not built with coverage enabled\n")
+ os.Exit(2)
+ }
+ if *gocoverdir != "" && CoverMode() == "" {
+ fmt.Fprintf(os.Stderr, "testing: cannot use -test.gocoverdir because test binary was not built with coverage enabled\n")
+ os.Exit(2)
+ }
+ if *testlog != "" {
+ // Note: Not using toOutputDir.
+ // This file is for use by cmd/go, not users.
+ var f *os.File
+ var err error
+ if m.numRun == 1 {
+ f, err = os.Create(*testlog)
+ } else {
+ f, err = os.OpenFile(*testlog, os.O_WRONLY, 0)
+ if err == nil {
+ f.Seek(0, io.SeekEnd)
+ }
+ }
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "testing: %s\n", err)
+ os.Exit(2)
+ }
+ m.deps.StartTestLog(f)
+ testlogFile = f
+ }
+ if *panicOnExit0 {
+ m.deps.SetPanicOnExit0(true)
+ }
+}
+
+// after runs after all testing.
+func (m *M) after() {
+ m.afterOnce.Do(func() {
+ m.writeProfiles()
+ })
+
+ // Restore PanicOnExit0 after every run, because we set it to true before
+ // every run. Otherwise, if m.Run is called multiple times the behavior of
+ // os.Exit(0) will not be restored after the second run.
+ if *panicOnExit0 {
+ m.deps.SetPanicOnExit0(false)
+ }
+}
+
+func (m *M) writeProfiles() {
+ if *testlog != "" {
+ if err := m.deps.StopTestLog(); err != nil {
+ fmt.Fprintf(os.Stderr, "testing: can't write %s: %s\n", *testlog, err)
+ os.Exit(2)
+ }
+ if err := testlogFile.Close(); err != nil {
+ fmt.Fprintf(os.Stderr, "testing: can't write %s: %s\n", *testlog, err)
+ os.Exit(2)
+ }
+ }
+ if *cpuProfile != "" {
+ m.deps.StopCPUProfile() // flushes profile to disk
+ }
+ if *traceFile != "" {
+ trace.Stop() // flushes trace to disk
+ }
+ if *memProfile != "" {
+ f, err := os.Create(toOutputDir(*memProfile))
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "testing: %s\n", err)
+ os.Exit(2)
+ }
+ runtime.GC() // materialize all statistics
+ if err = m.deps.WriteProfileTo("allocs", f, 0); err != nil {
+ fmt.Fprintf(os.Stderr, "testing: can't write %s: %s\n", *memProfile, err)
+ os.Exit(2)
+ }
+ f.Close()
+ }
+ if *blockProfile != "" && *blockProfileRate >= 0 {
+ f, err := os.Create(toOutputDir(*blockProfile))
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "testing: %s\n", err)
+ os.Exit(2)
+ }
+ if err = m.deps.WriteProfileTo("block", f, 0); err != nil {
+ fmt.Fprintf(os.Stderr, "testing: can't write %s: %s\n", *blockProfile, err)
+ os.Exit(2)
+ }
+ f.Close()
+ }
+ if *mutexProfile != "" && *mutexProfileFraction >= 0 {
+ f, err := os.Create(toOutputDir(*mutexProfile))
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "testing: %s\n", err)
+ os.Exit(2)
+ }
+ if err = m.deps.WriteProfileTo("mutex", f, 0); err != nil {
+ fmt.Fprintf(os.Stderr, "testing: can't write %s: %s\n", *mutexProfile, err)
+ os.Exit(2)
+ }
+ f.Close()
+ }
+ if CoverMode() != "" {
+ coverReport()
+ }
+}
+
+// toOutputDir returns the file name relocated, if required, to outputDir.
+// Simple implementation to avoid pulling in path/filepath.
+func toOutputDir(path string) string {
+ if *outputDir == "" || path == "" {
+ return path
+ }
+ // On Windows, it's clumsy, but we can be almost always correct
+ // by just looking for a drive letter and a colon.
+ // Absolute paths always have a drive letter (ignoring UNC).
+ // Problem: if path == "C:A" and outputdir == "C:\Go" it's unclear
+ // what to do, but even then path/filepath doesn't help.
+ // TODO: Worth doing better? Probably not, because we're here only
+ // under the management of go test.
+ if runtime.GOOS == "windows" && len(path) >= 2 {
+ letter, colon := path[0], path[1]
+ if ('a' <= letter && letter <= 'z' || 'A' <= letter && letter <= 'Z') && colon == ':' {
+ // If path starts with a drive letter we're stuck with it regardless.
+ return path
+ }
+ }
+ if os.IsPathSeparator(path[0]) {
+ return path
+ }
+ return fmt.Sprintf("%s%c%s", *outputDir, os.PathSeparator, path)
+}
+
+// startAlarm starts an alarm if requested.
+func (m *M) startAlarm() time.Time {
+ if *timeout <= 0 {
+ return time.Time{}
+ }
+
+ deadline := time.Now().Add(*timeout)
+ m.timer = time.AfterFunc(*timeout, func() {
+ m.after()
+ debug.SetTraceback("all")
+ extra := ""
+
+ if list := runningList(); len(list) > 0 {
+ var b strings.Builder
+ b.WriteString("\nrunning tests:")
+ for _, name := range list {
+ b.WriteString("\n\t")
+ b.WriteString(name)
+ }
+ extra = b.String()
+ }
+ panic(fmt.Sprintf("test timed out after %v%s", *timeout, extra))
+ })
+ return deadline
+}
+
+// runningList returns the list of running tests.
+func runningList() []string {
+ var list []string
+ running.Range(func(k, v any) bool {
+ list = append(list, fmt.Sprintf("%s (%v)", k.(string), time.Since(v.(time.Time)).Round(time.Second)))
+ return true
+ })
+ sort.Strings(list)
+ return list
+}
+
+// stopAlarm turns off the alarm.
+func (m *M) stopAlarm() {
+ if *timeout > 0 {
+ m.timer.Stop()
+ }
+}
+
+func parseCpuList() {
+ for _, val := range strings.Split(*cpuListStr, ",") {
+ val = strings.TrimSpace(val)
+ if val == "" {
+ continue
+ }
+ cpu, err := strconv.Atoi(val)
+ if err != nil || cpu <= 0 {
+ fmt.Fprintf(os.Stderr, "testing: invalid value %q for -test.cpu\n", val)
+ os.Exit(1)
+ }
+ cpuList = append(cpuList, cpu)
+ }
+ if cpuList == nil {
+ cpuList = append(cpuList, runtime.GOMAXPROCS(-1))
+ }
+}
+
+func shouldFailFast() bool {
+ return *failFast && numFailed.Load() > 0
+}
diff --git a/src/testing/testing_other.go b/src/testing/testing_other.go
new file mode 100644
index 0000000..99a6276
--- /dev/null
+++ b/src/testing/testing_other.go
@@ -0,0 +1,13 @@
+// Copyright 2021 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.
+
+//go:build !windows
+
+package testing
+
+// isWindowsRetryable reports whether err is a Windows error code
+// that may be fixed by retrying a failed filesystem operation.
+func isWindowsRetryable(err error) bool {
+ return false
+}
diff --git a/src/testing/testing_test.go b/src/testing/testing_test.go
new file mode 100644
index 0000000..d3822df
--- /dev/null
+++ b/src/testing/testing_test.go
@@ -0,0 +1,814 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testing_test
+
+import (
+ "bytes"
+ "fmt"
+ "internal/race"
+ "internal/testenv"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+ "slices"
+ "strings"
+ "sync"
+ "testing"
+ "time"
+)
+
+// This is exactly what a test would do without a TestMain.
+// It's here only so that there is at least one package in the
+// standard library with a TestMain, so that code is executed.
+
+func TestMain(m *testing.M) {
+ if os.Getenv("GO_WANT_RACE_BEFORE_TESTS") == "1" {
+ doRace()
+ }
+
+ m.Run()
+
+ // Note: m.Run currently prints the final "PASS" line, so if any race is
+ // reported here (after m.Run but before the process exits), it will print
+ // "PASS", then print the stack traces for the race, then exit with nonzero
+ // status.
+ //
+ // This is a somewhat fundamental race: because the race detector hooks into
+ // the runtime at a very low level, no matter where we put the printing it
+ // would be possible to report a race that occurs afterward. However, we could
+ // theoretically move the printing after TestMain, which would at least do a
+ // better job of diagnosing races in cleanup functions within TestMain itself.
+}
+
+func TestTempDirInCleanup(t *testing.T) {
+ var dir string
+
+ t.Run("test", func(t *testing.T) {
+ t.Cleanup(func() {
+ dir = t.TempDir()
+ })
+ _ = t.TempDir()
+ })
+
+ fi, err := os.Stat(dir)
+ if fi != nil {
+ t.Fatalf("Directory %q from user Cleanup still exists", dir)
+ }
+ if !os.IsNotExist(err) {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+}
+
+func TestTempDirInBenchmark(t *testing.T) {
+ testing.Benchmark(func(b *testing.B) {
+ if !b.Run("test", func(b *testing.B) {
+ // Add a loop so that the test won't fail. See issue 38677.
+ for i := 0; i < b.N; i++ {
+ _ = b.TempDir()
+ }
+ }) {
+ t.Fatal("Sub test failure in a benchmark")
+ }
+ })
+}
+
+func TestTempDir(t *testing.T) {
+ testTempDir(t)
+ t.Run("InSubtest", testTempDir)
+ t.Run("test/subtest", testTempDir)
+ t.Run("test\\subtest", testTempDir)
+ t.Run("test:subtest", testTempDir)
+ t.Run("test/..", testTempDir)
+ t.Run("../test", testTempDir)
+ t.Run("test[]", testTempDir)
+ t.Run("test*", testTempDir)
+ t.Run("äöüéè", testTempDir)
+}
+
+func testTempDir(t *testing.T) {
+ dirCh := make(chan string, 1)
+ t.Cleanup(func() {
+ // Verify directory has been removed.
+ select {
+ case dir := <-dirCh:
+ fi, err := os.Stat(dir)
+ if os.IsNotExist(err) {
+ // All good
+ return
+ }
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Errorf("directory %q still exists: %v, isDir=%v", dir, fi, fi.IsDir())
+ default:
+ if !t.Failed() {
+ t.Fatal("never received dir channel")
+ }
+ }
+ })
+
+ dir := t.TempDir()
+ if dir == "" {
+ t.Fatal("expected dir")
+ }
+ dir2 := t.TempDir()
+ if dir == dir2 {
+ t.Fatal("subsequent calls to TempDir returned the same directory")
+ }
+ if filepath.Dir(dir) != filepath.Dir(dir2) {
+ t.Fatalf("calls to TempDir do not share a parent; got %q, %q", dir, dir2)
+ }
+ dirCh <- dir
+ fi, err := os.Stat(dir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !fi.IsDir() {
+ t.Errorf("dir %q is not a dir", dir)
+ }
+ files, err := os.ReadDir(dir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(files) > 0 {
+ t.Errorf("unexpected %d files in TempDir: %v", len(files), files)
+ }
+
+ glob := filepath.Join(dir, "*.txt")
+ if _, err := filepath.Glob(glob); err != nil {
+ t.Error(err)
+ }
+}
+
+func TestSetenv(t *testing.T) {
+ tests := []struct {
+ name string
+ key string
+ initialValueExists bool
+ initialValue string
+ newValue string
+ }{
+ {
+ name: "initial value exists",
+ key: "GO_TEST_KEY_1",
+ initialValueExists: true,
+ initialValue: "111",
+ newValue: "222",
+ },
+ {
+ name: "initial value exists but empty",
+ key: "GO_TEST_KEY_2",
+ initialValueExists: true,
+ initialValue: "",
+ newValue: "222",
+ },
+ {
+ name: "initial value is not exists",
+ key: "GO_TEST_KEY_3",
+ initialValueExists: false,
+ initialValue: "",
+ newValue: "222",
+ },
+ }
+
+ for _, test := range tests {
+ if test.initialValueExists {
+ if err := os.Setenv(test.key, test.initialValue); err != nil {
+ t.Fatalf("unable to set env: got %v", err)
+ }
+ } else {
+ os.Unsetenv(test.key)
+ }
+
+ t.Run(test.name, func(t *testing.T) {
+ t.Setenv(test.key, test.newValue)
+ if os.Getenv(test.key) != test.newValue {
+ t.Fatalf("unexpected value after t.Setenv: got %s, want %s", os.Getenv(test.key), test.newValue)
+ }
+ })
+
+ got, exists := os.LookupEnv(test.key)
+ if got != test.initialValue {
+ t.Fatalf("unexpected value after t.Setenv cleanup: got %s, want %s", got, test.initialValue)
+ }
+ if exists != test.initialValueExists {
+ t.Fatalf("unexpected value after t.Setenv cleanup: got %t, want %t", exists, test.initialValueExists)
+ }
+ }
+}
+
+func TestSetenvWithParallelAfterSetenv(t *testing.T) {
+ defer func() {
+ want := "testing: t.Parallel called after t.Setenv; cannot set environment variables in parallel tests"
+ if got := recover(); got != want {
+ t.Fatalf("expected panic; got %#v want %q", got, want)
+ }
+ }()
+
+ t.Setenv("GO_TEST_KEY_1", "value")
+
+ t.Parallel()
+}
+
+func TestSetenvWithParallelBeforeSetenv(t *testing.T) {
+ defer func() {
+ want := "testing: t.Setenv called after t.Parallel; cannot set environment variables in parallel tests"
+ if got := recover(); got != want {
+ t.Fatalf("expected panic; got %#v want %q", got, want)
+ }
+ }()
+
+ t.Parallel()
+
+ t.Setenv("GO_TEST_KEY_1", "value")
+}
+
+func TestSetenvWithParallelParentBeforeSetenv(t *testing.T) {
+ t.Parallel()
+
+ t.Run("child", func(t *testing.T) {
+ defer func() {
+ want := "testing: t.Setenv called after t.Parallel; cannot set environment variables in parallel tests"
+ if got := recover(); got != want {
+ t.Fatalf("expected panic; got %#v want %q", got, want)
+ }
+ }()
+
+ t.Setenv("GO_TEST_KEY_1", "value")
+ })
+}
+
+func TestSetenvWithParallelGrandParentBeforeSetenv(t *testing.T) {
+ t.Parallel()
+
+ t.Run("child", func(t *testing.T) {
+ t.Run("grand-child", func(t *testing.T) {
+ defer func() {
+ want := "testing: t.Setenv called after t.Parallel; cannot set environment variables in parallel tests"
+ if got := recover(); got != want {
+ t.Fatalf("expected panic; got %#v want %q", got, want)
+ }
+ }()
+
+ t.Setenv("GO_TEST_KEY_1", "value")
+ })
+ })
+}
+
+// testingTrueInInit is part of TestTesting.
+var testingTrueInInit = false
+
+// testingTrueInPackageVarInit is part of TestTesting.
+var testingTrueInPackageVarInit = testing.Testing()
+
+// init is part of TestTesting.
+func init() {
+ if testing.Testing() {
+ testingTrueInInit = true
+ }
+}
+
+var testingProg = `
+package main
+
+import (
+ "fmt"
+ "testing"
+)
+
+func main() {
+ fmt.Println(testing.Testing())
+}
+`
+
+func TestTesting(t *testing.T) {
+ if !testing.Testing() {
+ t.Errorf("testing.Testing() == %t, want %t", testing.Testing(), true)
+ }
+ if !testingTrueInInit {
+ t.Errorf("testing.Testing() called by init function == %t, want %t", testingTrueInInit, true)
+ }
+ if !testingTrueInPackageVarInit {
+ t.Errorf("testing.Testing() variable initialized as %t, want %t", testingTrueInPackageVarInit, true)
+ }
+
+ if testing.Short() {
+ t.Skip("skipping building a binary in short mode")
+ }
+ testenv.MustHaveGoRun(t)
+
+ fn := filepath.Join(t.TempDir(), "x.go")
+ if err := os.WriteFile(fn, []byte(testingProg), 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "run", fn)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("%v failed: %v\n%s", cmd, err, out)
+ }
+
+ s := string(bytes.TrimSpace(out))
+ if s != "false" {
+ t.Errorf("in non-test testing.Test() returned %q, want %q", s, "false")
+ }
+}
+
+// runTest runs a helper test with -test.v, ignoring its exit status.
+// runTest both logs and returns the test output.
+func runTest(t *testing.T, test string) []byte {
+ t.Helper()
+
+ testenv.MustHaveExec(t)
+
+ exe, err := os.Executable()
+ if err != nil {
+ t.Skipf("can't find test executable: %v", err)
+ }
+
+ cmd := testenv.Command(t, exe, "-test.run=^"+test+"$", "-test.bench="+test, "-test.v", "-test.parallel=2", "-test.benchtime=2x")
+ cmd = testenv.CleanCmdEnv(cmd)
+ cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
+ out, err := cmd.CombinedOutput()
+ t.Logf("%v: %v\n%s", cmd, err, out)
+
+ return out
+}
+
+// doRace provokes a data race that generates a race detector report if run
+// under the race detector and is otherwise benign.
+func doRace() {
+ var x int
+ c1 := make(chan bool)
+ go func() {
+ x = 1 // racy write
+ c1 <- true
+ }()
+ _ = x // racy read
+ <-c1
+}
+
+func TestRaceReports(t *testing.T) {
+ if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
+ // Generate a race detector report in a sub test.
+ t.Run("Sub", func(t *testing.T) {
+ doRace()
+ })
+ return
+ }
+
+ out := runTest(t, "TestRaceReports")
+
+ // We should see at most one race detector report.
+ c := bytes.Count(out, []byte("race detected"))
+ want := 0
+ if race.Enabled {
+ want = 1
+ }
+ if c != want {
+ t.Errorf("got %d race reports, want %d", c, want)
+ }
+}
+
+// Issue #60083. This used to fail on the race builder.
+func TestRaceName(t *testing.T) {
+ if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
+ doRace()
+ return
+ }
+
+ out := runTest(t, "TestRaceName")
+
+ if regexp.MustCompile(`=== NAME\s*$`).Match(out) {
+ t.Errorf("incorrectly reported test with no name")
+ }
+}
+
+func TestRaceSubReports(t *testing.T) {
+ if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
+ t.Parallel()
+ c1 := make(chan bool, 1)
+ t.Run("sub", func(t *testing.T) {
+ t.Run("subsub1", func(t *testing.T) {
+ t.Parallel()
+ doRace()
+ c1 <- true
+ })
+ t.Run("subsub2", func(t *testing.T) {
+ t.Parallel()
+ doRace()
+ <-c1
+ })
+ })
+ doRace()
+ return
+ }
+
+ out := runTest(t, "TestRaceSubReports")
+
+ // There should be three race reports: one for each subtest, and one for the
+ // race after the subtests complete. Note that because the subtests run in
+ // parallel, the race stacks may both be printed in with one or the other
+ // test's logs.
+ cReport := bytes.Count(out, []byte("race detected during execution of test"))
+ wantReport := 0
+ if race.Enabled {
+ wantReport = 3
+ }
+ if cReport != wantReport {
+ t.Errorf("got %d race reports, want %d", cReport, wantReport)
+ }
+
+ // Regardless of when the stacks are printed, we expect each subtest to be
+ // marked as failed, and that failure should propagate up to the parents.
+ cFail := bytes.Count(out, []byte("--- FAIL:"))
+ wantFail := 0
+ if race.Enabled {
+ wantFail = 4
+ }
+ if cFail != wantFail {
+ t.Errorf(`got %d "--- FAIL:" lines, want %d`, cReport, wantReport)
+ }
+}
+
+func TestRaceInCleanup(t *testing.T) {
+ if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
+ t.Cleanup(doRace)
+ t.Parallel()
+ t.Run("sub", func(t *testing.T) {
+ t.Parallel()
+ // No race should be reported for sub.
+ })
+ return
+ }
+
+ out := runTest(t, "TestRaceInCleanup")
+
+ // There should be one race report, for the parent test only.
+ cReport := bytes.Count(out, []byte("race detected during execution of test"))
+ wantReport := 0
+ if race.Enabled {
+ wantReport = 1
+ }
+ if cReport != wantReport {
+ t.Errorf("got %d race reports, want %d", cReport, wantReport)
+ }
+
+ // Only the parent test should be marked as failed.
+ // (The subtest does not race, and should pass.)
+ cFail := bytes.Count(out, []byte("--- FAIL:"))
+ wantFail := 0
+ if race.Enabled {
+ wantFail = 1
+ }
+ if cFail != wantFail {
+ t.Errorf(`got %d "--- FAIL:" lines, want %d`, cReport, wantReport)
+ }
+}
+
+func TestDeepSubtestRace(t *testing.T) {
+ if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
+ t.Run("sub", func(t *testing.T) {
+ t.Run("subsub", func(t *testing.T) {
+ t.Run("subsubsub", func(t *testing.T) {
+ doRace()
+ })
+ })
+ doRace()
+ })
+ return
+ }
+
+ out := runTest(t, "TestDeepSubtestRace")
+
+ c := bytes.Count(out, []byte("race detected during execution of test"))
+ want := 0
+ // There should be two race reports.
+ if race.Enabled {
+ want = 2
+ }
+ if c != want {
+ t.Errorf("got %d race reports, want %d", c, want)
+ }
+}
+
+func TestRaceDuringParallelFailsAllSubtests(t *testing.T) {
+ if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
+ var ready sync.WaitGroup
+ ready.Add(2)
+ done := make(chan struct{})
+ go func() {
+ ready.Wait()
+ doRace() // This race happens while both subtests are running.
+ close(done)
+ }()
+
+ t.Run("sub", func(t *testing.T) {
+ t.Run("subsub1", func(t *testing.T) {
+ t.Parallel()
+ ready.Done()
+ <-done
+ })
+ t.Run("subsub2", func(t *testing.T) {
+ t.Parallel()
+ ready.Done()
+ <-done
+ })
+ })
+
+ return
+ }
+
+ out := runTest(t, "TestRaceDuringParallelFailsAllSubtests")
+
+ c := bytes.Count(out, []byte("race detected during execution of test"))
+ want := 0
+ // Each subtest should report the race independently.
+ if race.Enabled {
+ want = 2
+ }
+ if c != want {
+ t.Errorf("got %d race reports, want %d", c, want)
+ }
+}
+
+func TestRaceBeforeParallel(t *testing.T) {
+ if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
+ t.Run("sub", func(t *testing.T) {
+ doRace()
+ t.Parallel()
+ })
+ return
+ }
+
+ out := runTest(t, "TestRaceBeforeParallel")
+
+ c := bytes.Count(out, []byte("race detected during execution of test"))
+ want := 0
+ // We should see one race detector report.
+ if race.Enabled {
+ want = 1
+ }
+ if c != want {
+ t.Errorf("got %d race reports, want %d", c, want)
+ }
+}
+
+func TestRaceBeforeTests(t *testing.T) {
+ testenv.MustHaveExec(t)
+
+ exe, err := os.Executable()
+ if err != nil {
+ t.Skipf("can't find test executable: %v", err)
+ }
+
+ cmd := testenv.Command(t, exe, "-test.run=^$")
+ cmd = testenv.CleanCmdEnv(cmd)
+ cmd.Env = append(cmd.Env, "GO_WANT_RACE_BEFORE_TESTS=1")
+ out, _ := cmd.CombinedOutput()
+ t.Logf("%s", out)
+
+ c := bytes.Count(out, []byte("race detected outside of test execution"))
+
+ want := 0
+ if race.Enabled {
+ want = 1
+ }
+ if c != want {
+ t.Errorf("got %d race reports; want %d", c, want)
+ }
+}
+
+func TestBenchmarkRace(t *testing.T) {
+ out := runTest(t, "BenchmarkRacy")
+ c := bytes.Count(out, []byte("race detected during execution of test"))
+
+ want := 0
+ // We should see one race detector report.
+ if race.Enabled {
+ want = 1
+ }
+ if c != want {
+ t.Errorf("got %d race reports; want %d", c, want)
+ }
+}
+
+func BenchmarkRacy(b *testing.B) {
+ if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
+ b.Skipf("skipping intentionally-racy benchmark")
+ }
+ for i := 0; i < b.N; i++ {
+ doRace()
+ }
+}
+
+func TestBenchmarkSubRace(t *testing.T) {
+ out := runTest(t, "BenchmarkSubRacy")
+ c := bytes.Count(out, []byte("race detected during execution of test"))
+
+ want := 0
+ // We should see two race detector reports:
+ // one in the sub-bencmark, and one in the parent afterward.
+ if race.Enabled {
+ want = 2
+ }
+ if c != want {
+ t.Errorf("got %d race reports; want %d", c, want)
+ }
+}
+
+func BenchmarkSubRacy(b *testing.B) {
+ if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
+ b.Skipf("skipping intentionally-racy benchmark")
+ }
+
+ b.Run("non-racy", func(b *testing.B) {
+ tot := 0
+ for i := 0; i < b.N; i++ {
+ tot++
+ }
+ _ = tot
+ })
+
+ b.Run("racy", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ doRace()
+ }
+ })
+
+ doRace() // should be reported separately
+}
+
+func TestRunningTests(t *testing.T) {
+ t.Parallel()
+
+ // Regression test for https://go.dev/issue/64404:
+ // on timeout, the "running tests" message should not include
+ // tests that are waiting on parked subtests.
+
+ if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
+ for i := 0; i < 2; i++ {
+ t.Run(fmt.Sprintf("outer%d", i), func(t *testing.T) {
+ t.Parallel()
+ for j := 0; j < 2; j++ {
+ t.Run(fmt.Sprintf("inner%d", j), func(t *testing.T) {
+ t.Parallel()
+ for {
+ time.Sleep(1 * time.Millisecond)
+ }
+ })
+ }
+ })
+ }
+ }
+
+ timeout := 10 * time.Millisecond
+ for {
+ cmd := testenv.Command(t, os.Args[0], "-test.run=^"+t.Name()+"$", "-test.timeout="+timeout.String(), "-test.parallel=4")
+ cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1")
+ out, err := cmd.CombinedOutput()
+ t.Logf("%v:\n%s", cmd, out)
+ if _, ok := err.(*exec.ExitError); !ok {
+ t.Fatal(err)
+ }
+
+ // Because the outer subtests (and TestRunningTests itself) are marked as
+ // parallel, their test functions return (and are no longer “running”)
+ // before the inner subtests are released to run and hang.
+ // Only those inner subtests should be reported as running.
+ want := []string{
+ "TestRunningTests/outer0/inner0",
+ "TestRunningTests/outer0/inner1",
+ "TestRunningTests/outer1/inner0",
+ "TestRunningTests/outer1/inner1",
+ }
+
+ got, ok := parseRunningTests(out)
+ if slices.Equal(got, want) {
+ break
+ }
+ if ok {
+ t.Logf("found running tests:\n%s\nwant:\n%s", strings.Join(got, "\n"), strings.Join(want, "\n"))
+ } else {
+ t.Logf("no running tests found")
+ }
+ t.Logf("retrying with longer timeout")
+ timeout *= 2
+ }
+}
+
+func TestRunningTestsInCleanup(t *testing.T) {
+ t.Parallel()
+
+ if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
+ for i := 0; i < 2; i++ {
+ t.Run(fmt.Sprintf("outer%d", i), func(t *testing.T) {
+ // Not parallel: we expect to see only one outer test,
+ // stuck in cleanup after its subtest finishes.
+
+ t.Cleanup(func() {
+ for {
+ time.Sleep(1 * time.Millisecond)
+ }
+ })
+
+ for j := 0; j < 2; j++ {
+ t.Run(fmt.Sprintf("inner%d", j), func(t *testing.T) {
+ t.Parallel()
+ })
+ }
+ })
+ }
+ }
+
+ timeout := 10 * time.Millisecond
+ for {
+ cmd := testenv.Command(t, os.Args[0], "-test.run=^"+t.Name()+"$", "-test.timeout="+timeout.String())
+ cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1")
+ out, err := cmd.CombinedOutput()
+ t.Logf("%v:\n%s", cmd, out)
+ if _, ok := err.(*exec.ExitError); !ok {
+ t.Fatal(err)
+ }
+
+ // TestRunningTestsInCleanup is blocked in the call to t.Run,
+ // but its test function has not yet returned so it should still
+ // be considered to be running.
+ // outer1 hasn't even started yet, so only outer0 and the top-level
+ // test function should be reported as running.
+ want := []string{
+ "TestRunningTestsInCleanup",
+ "TestRunningTestsInCleanup/outer0",
+ }
+
+ got, ok := parseRunningTests(out)
+ if slices.Equal(got, want) {
+ break
+ }
+ if ok {
+ t.Logf("found running tests:\n%s\nwant:\n%s", strings.Join(got, "\n"), strings.Join(want, "\n"))
+ } else {
+ t.Logf("no running tests found")
+ }
+ t.Logf("retrying with longer timeout")
+ timeout *= 2
+ }
+}
+
+func parseRunningTests(out []byte) (runningTests []string, ok bool) {
+ inRunningTests := false
+ for _, line := range strings.Split(string(out), "\n") {
+ if inRunningTests {
+ if trimmed, ok := strings.CutPrefix(line, "\t"); ok {
+ if name, _, ok := strings.Cut(trimmed, " "); ok {
+ runningTests = append(runningTests, name)
+ continue
+ }
+ }
+
+ // This line is not the name of a running test.
+ return runningTests, true
+ }
+
+ if strings.TrimSpace(line) == "running tests:" {
+ inRunningTests = true
+ }
+ }
+
+ return nil, false
+}
+
+func TestConcurrentRun(t *testing.T) {
+ // Regression test for https://go.dev/issue/64402:
+ // this deadlocked after https://go.dev/cl/506755.
+
+ block := make(chan struct{})
+ var ready, done sync.WaitGroup
+ for i := 0; i < 2; i++ {
+ ready.Add(1)
+ done.Add(1)
+ go t.Run("", func(*testing.T) {
+ ready.Done()
+ <-block
+ done.Done()
+ })
+ }
+ ready.Wait()
+ close(block)
+ done.Wait()
+}
+
+func TestParentRun(t1 *testing.T) {
+ // Regression test for https://go.dev/issue/64402:
+ // this deadlocked after https://go.dev/cl/506755.
+
+ t1.Run("outer", func(t2 *testing.T) {
+ t2.Log("Hello outer!")
+ t1.Run("not_inner", func(t3 *testing.T) { // Note: this is t1.Run, not t2.Run.
+ t3.Log("Hello inner!")
+ })
+ })
+}
diff --git a/src/testing/testing_windows.go b/src/testing/testing_windows.go
new file mode 100644
index 0000000..fd48ae9
--- /dev/null
+++ b/src/testing/testing_windows.go
@@ -0,0 +1,32 @@
+// Copyright 2021 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.
+
+//go:build windows
+
+package testing
+
+import (
+ "errors"
+ "internal/syscall/windows"
+ "syscall"
+)
+
+// isWindowsRetryable reports whether err is a Windows error code
+// that may be fixed by retrying a failed filesystem operation.
+func isWindowsRetryable(err error) bool {
+ for {
+ unwrapped := errors.Unwrap(err)
+ if unwrapped == nil {
+ break
+ }
+ err = unwrapped
+ }
+ if err == syscall.ERROR_ACCESS_DENIED {
+ return true // Observed in https://go.dev/issue/50051.
+ }
+ if err == windows.ERROR_SHARING_VIOLATION {
+ return true // Observed in https://go.dev/issue/51442.
+ }
+ return false
+}