jayjyli
jayjyli

Reputation: 821

How do I properly use channels inside a parallelized Go test?

I'm experimenting with parallel subtests, and I'm hitting a goroutine deadlock error. Assuming that tt is a table test with valid values, and LastIndex is the function being tested, the following code runs all the tests, but errors out:

for _, tt := range tests {
    tt := tt
    t.Run("foo", func(t *testing.T) {
        t.Parallel()
        done := make(chan bool)
        for {
            select {
            case <-done:
                return
            case <-time.After(time.Second):
                if got := LastIndex(tt.list, tt.x); got != tt.want {
                    t.Fatalf("LastIndex(%v, %v) = %v, want %v", tt.list, tt.x, got, tt.want)
                } else {
                    done <- true
                }
            }
        }
    })
}

Specifically: fatal error: all goroutines are asleep - deadlock!

The error implies that the supertest is still waiting on results from these subtests, but shouldn't return and t.Fatalf() have exited them? The Go docs say that channels don't necessarily need to be closed, but I tried closing them anyway, and it still didn't work. I also tried to create the channel outside the subtest scope, inside the range iteration, and defer closing it with a t.Cleanup(), but that didn't work either.

Full code here: https://play.golang.org/p/1ujIIl7pjY9 Not all tests pass, this is intentional

What am I doing wrong?

Upvotes: 1

Views: 652

Answers (1)

Burak Serdar
Burak Serdar

Reputation: 51652

The t.Run call runs the test function in a separate goroutine and waits until it returns. The for-loop in the test writes to the done channel if a test is successful, however there are no other goroutines reading from the done channel, hence the deadlock.

Instead of writing to the done channel, you can close it. That will be detected next time around. However note that you are running only one goroutine, there is no concurrency here.

Upvotes: 1

Related Questions