Files

57 lines
1.3 KiB
Go

//go:build race
package websocket
import (
"context"
"sync"
"testing"
)
// TestDispatcher_ConcurrentRegisterAndDispatch exercises the Dispatcher
// from many goroutines at once. Its purpose is to surface a data race on
// the underlying handlers map when run under the race detector; the
// //go:build race tag scopes the test to `go test -race` runs (which CI
// uses for the backend) so the stress is not paid on plain `go test`
// invocations where it would add wall time without observing the race.
func TestDispatcher_ConcurrentRegisterAndDispatch(t *testing.T) {
d := NewDispatcher()
const goroutines = 16
const ops = 200
noop := HandlerFunc(func(_ context.Context, msg *Message) (*Message, error) {
return &Message{ID: msg.ID, Action: msg.Action}, nil
})
var wg sync.WaitGroup
wg.Add(goroutines * 2)
for g := 0; g < goroutines; g++ {
go func() {
defer wg.Done()
for i := 0; i < ops; i++ {
// Alternate between Register and RegisterFunc so both
// write paths are exercised under -race.
if i%2 == 0 {
d.Register("action", noop)
} else {
d.RegisterFunc("action", noop)
}
}
}()
go func() {
defer wg.Done()
for i := 0; i < ops; i++ {
_, _ = d.Dispatch(context.Background(), &Message{
ID: "x",
Action: "action",
})
_ = d.HasHandler("action")
}
}()
}
wg.Wait()
}