package bus_test import ( "sync" "sync/atomic" "testing" "github.com/badu/bus" ) func TestSubTopicWhilePub(t *testing.T) { // scenario : we have a large number of subscribers. // we publish an event and while doing that, // we register another one on a different goroutine topic := bus.NewTopic[*Uint32AsyncEvent]() for i := 0; i < 4096; i++ { topic.Sub(func(v *Uint32AsyncEvent) {}) } finishPubWait := make(chan struct{}) finishSubWait := make(chan struct{}) start := make(chan struct{}) go func() { <-start topic.PubAsync(&Uint32AsyncEvent{u: 1}) defer close(finishPubWait) }() newSubCalled := false go func() { <-start topic.Sub(func(v *Uint32AsyncEvent) { newSubCalled = true }) close(finishSubWait) }() close(start) // start both goroutines <-finishPubWait // wait for pub to finish <-finishSubWait // wait for sub to finish if newSubCalled { t.Fatal("new subscriber should not be called") } } func TestReusePayloadPointerAsync(t *testing.T) { // if you reuse the payload, you can alter it's content, of course topic := bus.NewTopic[*Uint32AsyncEvent]() c := uint32(0) for i := 0; i < 4096; i++ { k := i topic.Sub(func(v *Uint32AsyncEvent) { if v.u == 2048 { atomic.AddUint32(&c, 1) return } if k == 2048 { v.u = 2048 } }) } finishPubWait := make(chan struct{}) payload := Uint32AsyncEvent{u: 1} topic.Pub(&payload) close(finishPubWait) <-finishPubWait // wait for pub to finish t.Logf("altered payload %d for %d listeners", payload.u, c) } func TestAsyncBus(t *testing.T) { c := uint32(0) var wg sync.WaitGroup wg.Add(4096) bus.Sub( func(event Uint32AsyncEvent) { atomic.AddUint32(&c, 1) wg.Done() }, ) go func() { for i := 0; i < 1024; i++ { bus.PubAsync(Uint32AsyncEvent{}) } }() go func() { for i := 0; i < 1024; i++ { bus.PubAsync(Uint32AsyncEvent{}) } }() go func() { for i := 0; i < 1024; i++ { bus.PubAsync(Uint32AsyncEvent{}) } }() go func() { for i := 0; i < 1024; i++ { bus.PubAsync(Uint32AsyncEvent{}) } }() wg.Wait() if c != 4096 { t.Fatalf("error : counter should be 4096 but is %d", c) } t.Logf("%d", c) } func TestRange(t *testing.T) { type Event1 struct{} type Event2 struct{} type Event3 struct{} type Event4 struct{} type Event5 struct{} bus.Sub(func(e Event1) {}) bus.Sub(func(e Event2) {}) bus.Sub(func(e Event2) {}) bus.Sub(func(e Event3) {}) bus.Sub(func(e Event3) {}) bus.Sub(func(e Event3) {}) bus.Sub(func(e Event4) {}) bus.Sub(func(e Event4) {}) bus.Sub(func(e Event4) {}) bus.Sub(func(e Event4) {}) bus.Sub(func(e Event5) {}) bus.Sub(func(e Event5) {}) bus.Sub(func(e Event5) {}) bus.Sub(func(e Event5) {}) bus.Sub(func(e Event5) {}) seen := map[string]struct{}{ "bus_test.Event2": {}, "bus_test.Event3": {}, "bus_test.Event1": {}, "bus_test.Event5": {}, "bus_test.Event4": {}, } bus.Range(func(k, _ any) bool { if _, has := seen[k.(string)]; has { delete(seen, k.(string)) } return true }) if len(seen) > 0 { t.Fatalf("error : not all events were seen") } }