diff options
| author | Katie Hockman <katie@golang.org> | 2020-12-14 10:03:05 -0500 |
|---|---|---|
| committer | Katie Hockman <katie@golang.org> | 2020-12-14 10:06:13 -0500 |
| commit | 0345ede87ee12698988973884cfc0fd3d499dffd (patch) | |
| tree | 7123cff141ee5661208d2f5f437b8f5252ac7f6a /src/testing/testing.go | |
| parent | 4651d6b267818b0e0d128a5443289717c4bb8cbc (diff) | |
| parent | 0a02371b0576964e81c3b40d328db9a3ef3b031b (diff) | |
| download | go-0345ede87ee12698988973884cfc0fd3d499dffd.tar.xz | |
[dev.fuzz] all: merge master into dev.fuzz
Change-Id: I5d8c8329ccc9d747bd81ade6b1cb7cb8ae2e94b2
Diffstat (limited to 'src/testing/testing.go')
| -rw-r--r-- | src/testing/testing.go | 79 |
1 files changed, 58 insertions, 21 deletions
diff --git a/src/testing/testing.go b/src/testing/testing.go index 6267abfcdf..c87e0a5b9a 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -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 { |
