Commit 318812e7 authored by David Lazar's avatar David Lazar

testing: use function names to identify helpers

Previously, helpers were identified by entry PC, but this breaks if the
helper is inlined (as in notHelperCallingHelper). Instead, identify
helpers by function name (with package path). Now TestTBHelper and
TestTBHelperParallel pass with -l=4.

To keep the code unified, this change makes it so that the runner
is also identified by function name instead of entry PC.

Change-Id: I1b1987fc49d114e69d075fab56aeeacd5294982b
Reviewed-on: https://go-review.googlesource.com/41257
Run-TryBot: David Lazar <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarJosh Bleecher Snyder <josharian@gmail.com>
Reviewed-by: default avatarAustin Clements <austin@google.com>
parent 2397cd0f
...@@ -280,13 +280,13 @@ type common struct { ...@@ -280,13 +280,13 @@ type common struct {
failed bool // Test or benchmark has failed. failed bool // Test or benchmark has failed.
skipped bool // Test of benchmark has been skipped. skipped bool // Test of benchmark has been skipped.
done bool // Test is finished and all subtests have completed. done bool // Test is finished and all subtests have completed.
helpers map[uintptr]struct{} // functions to be skipped when writing file/line info helpers map[string]struct{} // functions to be skipped when writing file/line info
chatty bool // A copy of the chatty flag. chatty bool // A copy of the chatty flag.
finished bool // Test function has completed. finished bool // Test function has completed.
hasSub int32 // written atomically hasSub int32 // written atomically
raceErrors int // number of races detected during test raceErrors int // number of races detected during test
runner uintptr // entry pc of tRunner running the test runner string // function name of tRunner running the test
parent *common parent *common
level int // Nesting depth of test or benchmark. level int // Nesting depth of test or benchmark.
...@@ -336,14 +336,14 @@ func (c *common) frameSkip(skip int) int { ...@@ -336,14 +336,14 @@ func (c *common) frameSkip(skip int) int {
more := true more := true
for i := 0; more; i++ { for i := 0; more; i++ {
frame, more = frames.Next() frame, more = frames.Next()
if frame.Entry == c.runner { if frame.Function == c.runner {
// We've gone up all the way to the tRunner calling // We've gone up all the way to the tRunner calling
// the test function (so the user must have // the test function (so the user must have
// called tb.Helper from inside that test function). // called tb.Helper from inside that test function).
// Only skip up to the test function itself. // Only skip up to the test function itself.
return skip + i - 1 return skip + i - 1
} }
if _, ok := c.helpers[frame.Entry]; !ok { if _, ok := c.helpers[frame.Function]; !ok {
// Found a frame that wasn't inside a helper function. // Found a frame that wasn't inside a helper function.
return skip + i return skip + i
} }
...@@ -634,22 +634,23 @@ func (c *common) Helper() { ...@@ -634,22 +634,23 @@ func (c *common) Helper() {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
if c.helpers == nil { if c.helpers == nil {
c.helpers = make(map[uintptr]struct{}) c.helpers = make(map[string]struct{})
} }
c.helpers[callerEntry(1)] = struct{}{} c.helpers[callerName(1)] = struct{}{}
} }
// callerEntry gives the entry pc for the caller after skip frames // callerName gives the function name (qualified with a package path)
// (where 0 means the current function). // for the caller after skip frames (where 0 means the current function).
func callerEntry(skip int) uintptr { func callerName(skip int) string {
var pc [1]uintptr // Make room for the skip PC.
n := runtime.Callers(skip+2, pc[:]) // skip + runtime.Callers + callerEntry var pc [2]uintptr
n := runtime.Callers(skip+2, pc[:]) // skip + runtime.Callers + callerName
if n == 0 { if n == 0 {
panic("testing: zero callers found") panic("testing: zero callers found")
} }
frames := runtime.CallersFrames(pc[:]) frames := runtime.CallersFrames(pc[:n])
frame, _ := frames.Next() frame, _ := frames.Next()
return frame.Entry return frame.Function
} }
// Parallel signals that this test is to be run in parallel with (and only with) // Parallel signals that this test is to be run in parallel with (and only with)
...@@ -686,7 +687,7 @@ type InternalTest struct { ...@@ -686,7 +687,7 @@ type InternalTest struct {
} }
func tRunner(t *T, fn func(t *T)) { func tRunner(t *T, fn func(t *T)) {
t.runner = callerEntry(0) t.runner = callerName(0)
// When this goroutine is done, either because fn(t) // When this goroutine is done, either because fn(t)
// returned normally or because a test failure triggered // returned normally or because a test failure triggered
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment