github.com/mailgun/holster/v4@v4.20.0/syncutil/broadcast_test.go (about) 1 package syncutil_test 2 3 import ( 4 "fmt" 5 "sync" 6 "testing" 7 "time" 8 9 "github.com/mailgun/holster/v4/syncutil" 10 "github.com/stretchr/testify/assert" 11 ) 12 13 func TestBroadcaster(t *testing.T) { 14 t.Run("Happy path", func(t *testing.T) { 15 broadcaster := syncutil.NewBroadcaster() 16 ready := make(chan struct{}, 2) 17 done := make(chan struct{}) 18 socket := make(chan string, 11) 19 var mutex sync.Mutex 20 var chat []string 21 22 // Start some simple chat clients that are responsible for 23 // sending the contents of the []chat slice to their clients 24 for i := 0; i < 2; i++ { 25 go func(idx int) { 26 var clientIndex int 27 var once sync.Once 28 for { 29 mutex.Lock() 30 if clientIndex != len(chat) { 31 // Pretend we are sending a message to our client via a socket 32 socket <- fmt.Sprintf("Client [%d] Chat: %s\n", idx, chat[clientIndex]) 33 clientIndex++ 34 mutex.Unlock() 35 continue 36 } 37 mutex.Unlock() 38 39 // Indicate the client is up and ready to receive broadcasts 40 once.Do(func() { 41 ready <- struct{}{} 42 }) 43 44 // Wait for more chats to be added to chat[] 45 select { 46 case <-broadcaster.WaitChan(fmt.Sprint(idx)): 47 case <-done: 48 return 49 } 50 } 51 }(i) 52 } 53 54 // Wait for the clients to be ready 55 <-ready 56 <-ready 57 58 // Add some chat lines to the []chat slice 59 for i := 0; i < 5; i++ { 60 mutex.Lock() 61 chat = append(chat, fmt.Sprintf("Message '%d'", i)) 62 mutex.Unlock() 63 64 // Notify any clients there are new chats to read 65 broadcaster.Broadcast() 66 } 67 68 var count int 69 for msg := range socket { 70 t.Log(msg) 71 count++ 72 if count == 10 { 73 break 74 } 75 } 76 77 if count != 10 { 78 t.Errorf("count != 10") 79 } 80 // Tell the clients to quit 81 close(done) 82 }) 83 84 t.Run("Remove()", func(t *testing.T) { 85 const client1 = "Foobar1" 86 const client2 = "Foobar2" 87 88 t.Run("When using WaitChan()", func(t *testing.T) { 89 broadcaster := syncutil.NewBroadcaster() 90 91 // Register broadcast clients with WaitChan(). 92 ch1 := broadcaster.WaitChan(client1) 93 ch2 := broadcaster.WaitChan(client2) 94 95 // Test broadcast. 96 broadcaster.Broadcast() 97 assert.Len(t, ch1, 1) 98 assert.Len(t, ch2, 1) 99 <-ch1 100 <-ch2 101 102 // Remove a client. 103 broadcaster.Remove(client1) 104 105 // Test broadcast again. 106 // Verify client1 channel is unchanged. 107 broadcaster.Broadcast() 108 assert.Empty(t, ch1) 109 assert.Len(t, ch2, 1) 110 }) 111 112 t.Run("When using Wait()", func(t *testing.T) { 113 var doneWg sync.WaitGroup 114 broadcaster := syncutil.NewBroadcaster() 115 116 // Register broadcast clients with Wait(). 117 doneWg.Add(2) 118 var c1Count, c2Count int 119 120 go func() { 121 defer doneWg.Done() 122 broadcaster.Wait(client1) 123 c1Count++ 124 }() 125 go func() { 126 defer doneWg.Done() 127 broadcaster.Wait(client2) 128 c2Count++ 129 }() 130 131 for { 132 time.Sleep(1 * time.Millisecond) 133 if !broadcaster.Has(client1) { 134 continue 135 } 136 if !broadcaster.Has(client2) { 137 continue 138 } 139 break 140 } 141 142 // Test broadcast. 143 broadcaster.Broadcast() 144 doneWg.Wait() 145 assert.Equal(t, 1, c1Count) 146 assert.Equal(t, 1, c2Count) 147 148 // Get the generated channels. 149 ch1 := broadcaster.WaitChan(client1) 150 ch2 := broadcaster.WaitChan(client2) 151 152 // Remove a client. 153 broadcaster.Remove(client1) 154 155 // Test broadcast again. 156 // Verify client1 channel is unchanged. 157 broadcaster.Broadcast() 158 assert.Empty(t, ch1) 159 assert.Len(t, ch2, 1) 160 }) 161 }) 162 163 t.Run("WithChannelSize()", func(t *testing.T) { 164 t.Run("Default", func(t *testing.T) { 165 broadcaster := syncutil.NewBroadcaster() 166 ch := broadcaster.WaitChan("Foobar") 167 size := cap(ch) 168 assert.Equal(t, syncutil.DefaultChannelSize, size) 169 }) 170 171 t.Run("Custom", func(t *testing.T) { 172 const expectedChannelSize = 0xc0ffee 173 broadcaster := syncutil.NewBroadcaster(syncutil.WithChannelSize(expectedChannelSize)) 174 ch := broadcaster.WaitChan("Foobar") 175 size := cap(ch) 176 assert.Equal(t, expectedChannelSize, size) 177 }) 178 }) 179 }