mirror of
https://github.com/golang/go
synced 2024-11-18 04:04:49 -07:00
testing: make parallel t.Run safe again
Fixes #18603. Change-Id: I5760c0a9f862200b7e943058a672eb559ac1b9d9 Reviewed-on: https://go-review.googlesource.com/35354 Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
2c8b70eacf
commit
d10eddcba3
@ -219,7 +219,7 @@ func (b *B) run1() bool {
|
|||||||
}
|
}
|
||||||
// Only print the output if we know we are not going to proceed.
|
// Only print the output if we know we are not going to proceed.
|
||||||
// Otherwise it is printed in processBench.
|
// Otherwise it is printed in processBench.
|
||||||
if b.hasSub || b.finished {
|
if atomic.LoadInt32(&b.hasSub) != 0 || b.finished {
|
||||||
tag := "BENCH"
|
tag := "BENCH"
|
||||||
if b.skipped {
|
if b.skipped {
|
||||||
tag = "SKIP"
|
tag = "SKIP"
|
||||||
@ -460,10 +460,13 @@ func (ctx *benchContext) processBench(b *B) {
|
|||||||
//
|
//
|
||||||
// A subbenchmark is like any other benchmark. A benchmark that calls Run at
|
// A subbenchmark is like any other benchmark. A benchmark that calls Run at
|
||||||
// least once will not be measured itself and will be called once with N=1.
|
// least once will not be measured itself and will be called once with N=1.
|
||||||
|
//
|
||||||
|
// Run may be called simultaneously from multiple goroutines, but all such
|
||||||
|
// calls must happen before the outer benchmark function for b returns.
|
||||||
func (b *B) Run(name string, f func(b *B)) bool {
|
func (b *B) Run(name string, f func(b *B)) bool {
|
||||||
// Since b has subbenchmarks, we will no longer run it as a benchmark itself.
|
// Since b has subbenchmarks, we will no longer run it as a benchmark itself.
|
||||||
// Release the lock and acquire it on exit to ensure locks stay paired.
|
// Release the lock and acquire it on exit to ensure locks stay paired.
|
||||||
b.hasSub = true
|
atomic.StoreInt32(&b.hasSub, 1)
|
||||||
benchmarkLock.Unlock()
|
benchmarkLock.Unlock()
|
||||||
defer benchmarkLock.Lock()
|
defer benchmarkLock.Lock()
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ package testing
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
@ -515,3 +516,19 @@ func TestBenchmarkOutput(t *T) {
|
|||||||
Benchmark(func(b *B) { b.Error("do not print this output") })
|
Benchmark(func(b *B) { b.Error("do not print this output") })
|
||||||
Benchmark(func(b *B) {})
|
Benchmark(func(b *B) {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParallelSub(t *T) {
|
||||||
|
c := make(chan int)
|
||||||
|
block := make(chan int)
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
go func(i int) {
|
||||||
|
<-block
|
||||||
|
t.Run(fmt.Sprint(i), func(t *T) {})
|
||||||
|
c <- 1
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
close(block)
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
<-c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -216,6 +216,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -267,8 +268,8 @@ type common struct {
|
|||||||
skipped bool // Test of benchmark has been skipped.
|
skipped bool // Test of benchmark has been skipped.
|
||||||
finished bool // Test function has completed.
|
finished bool // Test function has completed.
|
||||||
done bool // Test is finished and all subtests have completed.
|
done bool // Test is finished and all subtests have completed.
|
||||||
hasSub bool
|
hasSub int32 // written atomically
|
||||||
raceErrors int // number of races detected during test
|
raceErrors int // number of races detected during test
|
||||||
|
|
||||||
parent *common
|
parent *common
|
||||||
level int // Nesting depth of test or benchmark.
|
level int // Nesting depth of test or benchmark.
|
||||||
@ -645,7 +646,7 @@ func tRunner(t *T, fn func(t *T)) {
|
|||||||
// Do not lock t.done to allow race detector to detect race in case
|
// Do not lock t.done to allow race detector to detect race in case
|
||||||
// the user does not appropriately synchronizes a goroutine.
|
// the user does not appropriately synchronizes a goroutine.
|
||||||
t.done = true
|
t.done = true
|
||||||
if t.parent != nil && !t.hasSub {
|
if t.parent != nil && atomic.LoadInt32(&t.hasSub) == 0 {
|
||||||
t.setRan()
|
t.setRan()
|
||||||
}
|
}
|
||||||
t.signal <- true
|
t.signal <- true
|
||||||
@ -659,8 +660,11 @@ func tRunner(t *T, fn func(t *T)) {
|
|||||||
|
|
||||||
// Run runs f as a subtest of t called name. It reports whether f succeeded.
|
// Run runs f as a subtest of t called name. It reports whether f succeeded.
|
||||||
// Run will block until all its parallel subtests have completed.
|
// Run will block until all its parallel subtests have completed.
|
||||||
|
//
|
||||||
|
// Run may be called simultaneously from multiple goroutines, but all such
|
||||||
|
// calls must happen before the outer test function for t returns.
|
||||||
func (t *T) Run(name string, f func(t *T)) bool {
|
func (t *T) Run(name string, f func(t *T)) bool {
|
||||||
t.hasSub = true
|
atomic.StoreInt32(&t.hasSub, 1)
|
||||||
testName, ok := t.context.match.fullName(&t.common, name)
|
testName, ok := t.context.match.fullName(&t.common, name)
|
||||||
if !ok {
|
if !ok {
|
||||||
return true
|
return true
|
||||||
|
Loading…
Reference in New Issue
Block a user