Commit 7b9d3ff4 authored by Marcel van Lohuizen's avatar Marcel van Lohuizen

testing: don't be silent if a test's goroutine fails a test after test exits

Fixes #15654

Change-Id: I9bdaa9b76d480d75f24d95f0235efd4a79e3593e
Reviewed-on: https://go-review.googlesource.com/23320Reviewed-by: 's avatarRuss Cox <rsc@golang.org>
Run-TryBot: Marcel van Lohuizen <mpvl@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarJoe Tsai <thebrokentoaster@gmail.com>
parent dcc42c7d
...@@ -307,6 +307,27 @@ func TestTRun(t *T) { ...@@ -307,6 +307,27 @@ func TestTRun(t *T) {
f: func(t *T) { f: func(t *T) {
t.Skip() t.Skip()
}, },
}, {
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
},
}} }}
for _, tc := range testCases { for _, tc := range testCases {
ctx := newTestContext(tc.maxPar, newMatcher(regexp.MatchString, "", "")) ctx := newTestContext(tc.maxPar, newMatcher(regexp.MatchString, "", ""))
......
...@@ -196,13 +196,14 @@ var ( ...@@ -196,13 +196,14 @@ var (
// common holds the elements common between T and B and // common holds the elements common between T and B and
// captures common methods such as Errorf. // captures common methods such as Errorf.
type common struct { type common struct {
mu sync.RWMutex // guards output and failed mu sync.RWMutex // guards output, failed, and done.
output []byte // Output generated by test or benchmark. output []byte // Output generated by test or benchmark.
w io.Writer // For flushToParent. w io.Writer // For flushToParent.
chatty bool // A copy of the chatty flag. chatty bool // A copy of the chatty flag.
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.
finished bool finished bool // Test function has completed.
done bool // Test is finished and all subtests have completed.
parent *common parent *common
level int // Nesting depth of test or benchmark. level int // Nesting depth of test or benchmark.
...@@ -351,6 +352,10 @@ func (c *common) Fail() { ...@@ -351,6 +352,10 @@ func (c *common) Fail() {
} }
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() 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 c.failed = true
} }
...@@ -540,6 +545,9 @@ func tRunner(t *T, fn func(t *T)) { ...@@ -540,6 +545,9 @@ func tRunner(t *T, fn func(t *T)) {
} }
t.report() // Report after all subtests have finished. 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 synchronizes a goroutine.
t.done = true
t.signal <- true t.signal <- true
}() }()
......
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