})
}
-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
- }
-}
-
type funcWriter struct {
write func([]byte) (int, error)
}
func TestConcurrentCleanup(t *T) {
cleanups := 0
t.Run("test", func(t *T) {
- done := make(chan struct{})
+ var wg sync.WaitGroup
+ wg.Add(2)
for i := 0; i < 2; i++ {
i := i
go func() {
t.Cleanup(func() {
+ // Although the calls to Cleanup are concurrent, the functions passed
+ // to Cleanup should be called sequentially, in some nondeterministic
+ // order based on when the Cleanup calls happened to be scheduled.
+ // So these assignments to the cleanups variable should not race.
cleanups |= 1 << i
})
- done <- struct{}{}
+ wg.Done()
}()
}
- <-done
- <-done
+ wg.Wait()
})
if cleanups != 1|2 {
t.Errorf("unexpected cleanup; got %d want 3", cleanups)
return nil, false
}
+
+func TestConcurrentRun(t *testing.T) {
+ // Regression test for https://go.dev/issue/64402:
+ // this deadlocked after https://go.dev/cl/506755.
+
+ block := make(chan struct{})
+ var ready, done sync.WaitGroup
+ for i := 0; i < 2; i++ {
+ ready.Add(1)
+ done.Add(1)
+ go t.Run("", func(*testing.T) {
+ ready.Done()
+ <-block
+ done.Done()
+ })
+ }
+ ready.Wait()
+ close(block)
+ done.Wait()
+}
+
+func TestParentRun(t1 *testing.T) {
+ // Regression test for https://go.dev/issue/64402:
+ // this deadlocked after https://go.dev/cl/506755.
+
+ t1.Run("outer", func(t2 *testing.T) {
+ t2.Log("Hello outer!")
+ t1.Run("not_inner", func(t3 *testing.T) { // Note: this is t1.Run, not t2.Run.
+ t3.Log("Hello inner!")
+ })
+ })
+}