"fmt"
"internal/race"
"io"
- "io/ioutil"
"os"
"runtime"
"runtime/debug"
// 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.
}
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
}
// 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
}
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
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 {
// 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
}
// 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 {
}
}
}
+ // 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()
fmt.Fprintf(root.parent.w, "cleanup panicked with %v", r)
}
}
+ didPanic = true
panic(err)
}
if err != nil {
if t.parent != nil && atomic.LoadInt32(&t.hasSub) == 0 {
t.setRan()
}
- t.signal <- signal
}()
defer func() {
if len(t.sub) == 0 {