]> Cypherpunks.ru repositories - gostls13.git/blobdiff - src/testing/testing.go
[dev.fuzz] all: merge master into dev.fuzz
[gostls13.git] / src / testing / testing.go
index 6267abfcdff9dc76504164dc3a9b2485e9a7aaf9..c87e0a5b9ac588fcd6f519b4500e0ce083bc0620 100644 (file)
@@ -242,7 +242,6 @@ import (
        "fmt"
        "internal/race"
        "io"
-       "io/ioutil"
        "os"
        "runtime"
        "runtime/debug"
@@ -385,17 +384,18 @@ 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 of benchmark has been skipped.
-       done        bool                // Test is finished and all subtests have completed.
-       helpers     map[string]struct{} // functions to be skipped when writing file/line info
-       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.
+       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 of 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.
 
        chatty     *chattyPrinter // A copy of chattyPrinter, if the chatty flag is set.
        bench      bool           // Whether the current test is a benchmark.
@@ -511,7 +511,7 @@ func (c *common) frameSkip(skip int) runtime.Frame {
                        }
                        return prevFrame
                }
-               if _, ok := c.helpers[frame.Function]; !ok {
+               if _, ok := c.helperNames[frame.Function]; !ok {
                        // Found a frame that wasn't inside a helper function.
                        return frame
                }
@@ -523,6 +523,14 @@ func (c *common) frameSkip(skip int) runtime.Frame {
 // 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 {
+       // 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{}{}
+               }
+       }
+
        frame := c.frameSkip(skip)
        file := frame.File
        line := frame.Line
@@ -885,10 +893,19 @@ func (c *common) Helper() {
        }
        c.mu.Lock()
        defer c.mu.Unlock()
-       if c.helpers == nil {
-               c.helpers = make(map[string]struct{})
+       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
        }
-       c.helpers[callerName(1)] = struct{}{}
 }
 
 // Cleanup registers a function to be called when the test and all its
@@ -956,14 +973,14 @@ func (c *common) TempDir() string {
        if nonExistent {
                c.Helper()
 
-               // ioutil.TempDir doesn't like path separators in its pattern,
+               // os.MkdirTemp doesn't like path separators in its pattern,
                // so mangle the name to accommodate subtests.
                tempDirReplacer.Do(func() {
                        tempDirReplacer.r = strings.NewReplacer("/", "_", "\\", "_", ":", "_")
                })
                pattern := tempDirReplacer.r.Replace(c.Name())
 
-               c.tempDir, c.tempDirErr = ioutil.TempDir("", pattern)
+               c.tempDir, c.tempDirErr = os.MkdirTemp("", pattern)
                if c.tempDirErr == nil {
                        c.Cleanup(func() {
                                if err := os.RemoveAll(c.tempDir); err != nil {
@@ -1033,13 +1050,17 @@ func (c *common) runCleanup(ph panicHandling) (panicVal interface{}) {
 // 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 {
-       // Make room for the skip PC.
        var pc [1]uintptr
        n := runtime.Callers(skip+2, pc[:]) // skip + runtime.Callers + callerName
        if n == 0 {
                panic("testing: zero callers found")
        }
-       frames := runtime.CallersFrames(pc[:n])
+       return pcToName(pc[0])
+}
+
+func pcToName(pc uintptr) string {
+       pcs := []uintptr{pc}
+       frames := runtime.CallersFrames(pcs)
        frame, _ := frames.Next()
        return frame.Function
 }
@@ -1113,6 +1134,7 @@ func tRunner(t *T, fn func(t *T)) {
                // If the test panicked, print any test output before dying.
                err := recover()
                signal := true
+
                if !t.finished && err == nil {
                        err = errNilPanicOrGoexit
                        for p := t.parent; p != nil; p = p.parent {
@@ -1124,6 +1146,21 @@ func tRunner(t *T, fn func(t *T)) {
                                }
                        }
                }
+               // 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() {
+                       if didPanic {
+                               return
+                       }
+                       if err != nil {
+                               panic(err)
+                       }
+                       // 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.
+                       t.signal <- signal
+               }()
 
                doPanic := func(err interface{}) {
                        t.Fail()
@@ -1141,6 +1178,7 @@ func tRunner(t *T, fn func(t *T)) {
                                        fmt.Fprintf(root.parent.w, "cleanup panicked with %v", r)
                                }
                        }
+                       didPanic = true
                        panic(err)
                }
                if err != nil {
@@ -1182,7 +1220,6 @@ func tRunner(t *T, fn func(t *T)) {
                if t.parent != nil && atomic.LoadInt32(&t.hasSub) == 0 {
                        t.setRan()
                }
-               t.signal <- signal
        }()
        defer func() {
                if len(t.sub) == 0 {