github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/events/events_test.go (about) 1 package events 2 3 import ( 4 "fmt" 5 "testing" 6 "time" 7 8 "github.com/stretchr/testify/assert" 9 "github.com/stretchr/testify/require" 10 11 "github.com/gnolang/gno/tm2/pkg/random" 12 ) 13 14 // TestAddListenerFireOnce sets up an EventSwitch, subscribes a single 15 // listener, and sends a string "ev". 16 func TestAddListenerFireOnce(t *testing.T) { 17 evsw := NewEventSwitch() 18 err := evsw.Start() 19 require.NoError(t, err) 20 defer evsw.Stop() 21 22 messages := make(chan Event) 23 evsw.AddListener("listener", func(ev Event) { 24 // test there's no deadlock if we remove the listener inside a callback 25 evsw.RemoveListener("listener") 26 messages <- ev 27 }) 28 go evsw.FireEvent(StringEvent("ev")) 29 received := <-messages 30 if received != StringEvent("ev") { 31 t.Errorf("Message received does not match: %v", received) 32 } 33 } 34 35 // TestAddListenerFireMany sets up an EventSwitch, subscribes a single 36 // listener, and sends a thousand integers. 37 func TestAddListenerFireMany(t *testing.T) { 38 t.Parallel() 39 40 evsw := NewEventSwitch() 41 err := evsw.Start() 42 require.NoError(t, err) 43 defer evsw.Stop() 44 45 doneSum := make(chan uint64) 46 doneSending := make(chan uint64) 47 numbers := make(chan uint64, 4) 48 // subscribe one listener for one event 49 evsw.AddListener("listener", func(ev Event) { 50 numbers <- uint64(ev.(Uint64Event)) 51 }) 52 // collect received events 53 go sumReceivedNumbers(numbers, doneSum) 54 // go fire events 55 go fireEvents(evsw, doneSending, uint64(1)) 56 checkSum := <-doneSending 57 close(numbers) 58 eventSum := <-doneSum 59 if checkSum != eventSum { 60 t.Errorf("Not all messages sent were received.\n") 61 } 62 } 63 64 // TestAddListeners sets up an EventSwitch, subscribes three 65 // listeners, and sends a thousand integers for each. 66 func TestAddListeners(t *testing.T) { 67 t.Parallel() 68 69 evsw := NewEventSwitch() 70 err := evsw.Start() 71 require.NoError(t, err) 72 defer evsw.Stop() 73 74 doneSum := make(chan uint64) 75 doneSending1 := make(chan uint64) 76 doneSending2 := make(chan uint64) 77 doneSending3 := make(chan uint64) 78 numbers := make(chan uint64, 4) 79 // subscribe one listener to three events 80 evsw.AddListener("listener", func(ev Event) { 81 numbers <- uint64(ev.(Uint64Event)) 82 }) 83 evsw.AddListener("listener", func(ev Event) { 84 numbers <- uint64(ev.(Uint64Event)) 85 }) 86 evsw.AddListener("listener", func(ev Event) { 87 numbers <- uint64(ev.(Uint64Event)) 88 }) 89 // collect received events 90 go sumReceivedNumbers(numbers, doneSum) 91 // go fire events 92 go fireEvents(evsw, doneSending1, uint64(1)) 93 go fireEvents(evsw, doneSending2, uint64(1)) 94 go fireEvents(evsw, doneSending3, uint64(1)) 95 var checkSum uint64 = 0 96 checkSum += <-doneSending1 97 checkSum += <-doneSending2 98 checkSum += <-doneSending3 99 close(numbers) 100 eventSum := <-doneSum 101 if checkSum*3 != eventSum { 102 t.Errorf("Not all messages sent were received.\n") 103 } 104 } 105 106 func TestAddAndRemoveListenerConcurrency(t *testing.T) { 107 t.Parallel() 108 109 var ( 110 stopInputEvent = false 111 roundCount = 2000 112 ) 113 114 evsw := NewEventSwitch() 115 err := evsw.Start() 116 require.NoError(t, err) 117 defer evsw.Stop() 118 119 done1 := make(chan struct{}) 120 done2 := make(chan struct{}) 121 122 // Must be executed concurrently to uncover the ev race. 123 // 1. RemoveListener 124 go func() { 125 for i := 0; i < roundCount; i++ { 126 evsw.RemoveListener("listener") 127 } 128 close(done1) 129 }() 130 131 // 2. AddListener 132 go func() { 133 for i := 0; i < roundCount; i++ { 134 index := i 135 evsw.AddListener("listener", func(ev Event) { 136 t.Errorf("should not run callback for %d.\n", index) 137 stopInputEvent = true 138 }) 139 } 140 close(done2) 141 }() 142 143 <-done1 144 <-done2 145 146 evsw.RemoveListener("listener") // remove the last listener 147 148 for i := 0; i < roundCount && !stopInputEvent; i++ { 149 evsw.FireEvent(Uint64Event(uint64(1001))) 150 } 151 } 152 153 func TestAddAndRemoveListener(t *testing.T) { 154 t.Parallel() 155 156 evsw := NewEventSwitch() 157 err := evsw.Start() 158 require.NoError(t, err) 159 defer evsw.Stop() 160 161 doneSum1 := make(chan uint64) 162 doneSum2 := make(chan uint64) 163 doneSending1 := make(chan uint64) 164 doneSending2 := make(chan uint64) 165 numbers1 := make(chan uint64, 4) 166 numbers2 := make(chan uint64, 4) 167 // subscribe two listener to three events 168 evsw.AddListener("listener", func(ev Event) { 169 if uint64(ev.(Uint64Event)) <= 1000 { 170 numbers1 <- uint64(ev.(Uint64Event)) 171 } 172 }) 173 evsw.AddListener("listener", func(ev Event) { 174 if uint64(ev.(Uint64Event)) > 1000 { 175 numbers2 <- uint64(ev.(Uint64Event)) 176 } 177 }) 178 // collect received events for event1 179 go sumReceivedNumbers(numbers1, doneSum1) 180 // collect received events for event2 181 go sumReceivedNumbers(numbers2, doneSum2) 182 // go fire events 183 go fireEvents(evsw, doneSending1, uint64(1)) // to numbers1. 184 checkSumEvent1 := <-doneSending1 185 // after sending all event1, unsubscribe for all events 186 evsw.RemoveListener("listener") 187 go fireEvents(evsw, doneSending2, uint64(1001)) // would be to numbers2. 188 checkSumEvent2 := <-doneSending2 189 close(numbers1) 190 close(numbers2) 191 eventSum1 := <-doneSum1 192 eventSum2 := <-doneSum2 193 if checkSumEvent1 != eventSum1 || 194 // correct value asserted by preceding tests, suffices to be non-zero 195 checkSumEvent2 == uint64(0) || 196 eventSum2 != uint64(0) { 197 t.Errorf("Not all messages sent were received or unsubscription did not register.\n") 198 } 199 } 200 201 // TestRemoveListener does basic tests on adding and removing 202 func TestRemoveListener(t *testing.T) { 203 t.Parallel() 204 205 evsw := NewEventSwitch() 206 err := evsw.Start() 207 require.NoError(t, err) 208 defer evsw.Stop() 209 210 count := 10 211 sum1, sum2 := 0, 0 212 // add some listeners and make sure they work 213 evsw.AddListener("BEEP-listener", func(ev Event) { 214 if string(ev.(StringEvent)) == "BEEP" { 215 sum1++ 216 } 217 }) 218 evsw.AddListener("boop-listener", func(ev Event) { 219 if string(ev.(StringEvent)) == "boop" { 220 sum2++ 221 } 222 }) 223 for i := 0; i < count; i++ { 224 evsw.FireEvent(StringEvent("BEEP")) 225 evsw.FireEvent(StringEvent("boop")) 226 } 227 assert.Equal(t, count, sum1) 228 assert.Equal(t, count, sum2) 229 230 // remove one by event and make sure it is gone 231 evsw.RemoveListener("boop-listener") 232 for i := 0; i < count; i++ { 233 evsw.FireEvent(StringEvent("BEEP")) 234 evsw.FireEvent(StringEvent("boop")) 235 } 236 assert.Equal(t, count*2, sum1) 237 assert.Equal(t, count, sum2) 238 239 // remove the listener entirely and make sure both gone 240 evsw.RemoveListener("BEEP-listener") 241 for i := 0; i < count; i++ { 242 evsw.FireEvent(StringEvent("BEEP")) 243 evsw.FireEvent(StringEvent("boop")) 244 } 245 assert.Equal(t, count*2, sum1) 246 assert.Equal(t, count, sum2) 247 } 248 249 // More precisely it randomly subscribes new listeners. 250 // At the same time it starts randomly unsubscribing these additional listeners. 251 // NOTE: it is important to run this test with race conditions tracking on, 252 // `go test -race`, to examine for possible race conditions. 253 func TestRemoveListenersAsync(t *testing.T) { 254 t.Parallel() 255 256 evsw := NewEventSwitch() 257 err := evsw.Start() 258 require.NoError(t, err) 259 defer evsw.Stop() 260 261 doneSum1 := make(chan uint64) 262 doneSum2 := make(chan uint64) 263 doneSending1 := make(chan uint64) 264 doneSending2 := make(chan uint64) 265 doneSending3 := make(chan uint64) 266 numbers1 := make(chan uint64, 4) 267 numbers2 := make(chan uint64, 4) 268 // subscribe two listener to three events 269 evsw.AddListener("listener1", func(ev Event) { 270 evi := uint64(ev.(Uint64Event)) 271 if 1 <= evi && evi <= 1000 { 272 numbers1 <- uint64(ev.(Uint64Event)) 273 } 274 }) 275 evsw.AddListener("listener1", func(ev Event) { 276 evi := uint64(ev.(Uint64Event)) 277 if 1001 <= evi && evi <= 2000 { 278 numbers1 <- uint64(ev.(Uint64Event)) 279 } 280 }) 281 evsw.AddListener("listener1", func(ev Event) { 282 evi := uint64(ev.(Uint64Event)) 283 if 2001 <= evi && evi <= 3000 { 284 numbers1 <- uint64(ev.(Uint64Event)) 285 } 286 }) 287 evsw.AddListener("listener2", func(ev Event) { 288 evi := uint64(ev.(Uint64Event)) 289 if 1 <= evi && evi <= 1000 { 290 numbers2 <- uint64(ev.(Uint64Event)) 291 } 292 }) 293 evsw.AddListener("listener2", func(ev Event) { 294 evi := uint64(ev.(Uint64Event)) 295 if 1001 <= evi && evi <= 2000 { 296 numbers2 <- uint64(ev.(Uint64Event)) 297 } 298 }) 299 evsw.AddListener("listener2", func(ev Event) { 300 evi := uint64(ev.(Uint64Event)) 301 if 2001 <= evi && evi <= 3000 { 302 numbers2 <- uint64(ev.(Uint64Event)) 303 } 304 }) 305 // collect received events for event1 306 go sumReceivedNumbers(numbers1, doneSum1) 307 // collect received events for event2 308 go sumReceivedNumbers(numbers2, doneSum2) 309 addListenersStress := func() { 310 r1 := random.NewRand() 311 r1.Seed(time.Now().UnixNano()) 312 for k := uint16(0); k < 400; k++ { 313 listenerNumber := r1.Intn(100) + 3 314 go evsw.AddListener(fmt.Sprintf("listener%v", listenerNumber), 315 func(_ Event) {}) 316 } 317 } 318 removeListenersStress := func() { 319 r2 := random.NewRand() 320 r2.Seed(time.Now().UnixNano()) 321 for k := uint16(0); k < 80; k++ { 322 listenerNumber := r2.Intn(100) + 3 323 go evsw.RemoveListener(fmt.Sprintf("listener%v", listenerNumber)) 324 } 325 } 326 addListenersStress() 327 // go fire events 328 go fireEvents(evsw, doneSending1, uint64(1)) 329 removeListenersStress() 330 go fireEvents(evsw, doneSending2, uint64(1001)) 331 go fireEvents(evsw, doneSending3, uint64(2001)) 332 checkSumEvent1 := <-doneSending1 333 checkSumEvent2 := <-doneSending2 334 checkSumEvent3 := <-doneSending3 335 checkSum := checkSumEvent1 + checkSumEvent2 + checkSumEvent3 336 close(numbers1) 337 close(numbers2) 338 eventSum1 := <-doneSum1 339 eventSum2 := <-doneSum2 340 if checkSum != eventSum1 || 341 checkSum != eventSum2 { 342 t.Errorf("Not all messages sent were received.\n") 343 } 344 } 345 346 // ------------------------------------------------------------------------------ 347 // Helper functions 348 349 // sumReceivedNumbers takes two channels and adds all numbers received 350 // until the receiving channel `numbers` is closed; it then sends the sum 351 // on `doneSum` and closes that channel. Expected to be run in a go-routine. 352 func sumReceivedNumbers(numbers, doneSum chan uint64) { 353 var sum uint64 354 for { 355 j, more := <-numbers 356 sum += j 357 if !more { 358 doneSum <- sum 359 close(doneSum) 360 return 361 } 362 } 363 } 364 365 // fireEvents takes an EventSwitch and fires a thousand integers with the 366 // integers mootonically increasing from `offset` to `offset` + 999. It 367 // additionally returns the addition of all integers sent on `doneChan` for 368 // assertion that all events have been sent, and enabling the test to assert 369 // all events have also been received. 370 func fireEvents(evsw EventSwitch, doneChan chan uint64, 371 offset uint64, 372 ) { 373 var sentSum uint64 374 for i := offset; i <= offset+uint64(999); i++ { 375 sentSum += i 376 evsw.FireEvent(Uint64Event(i)) 377 } 378 doneChan <- sentSum 379 close(doneChan) 380 } 381 382 type Uint64Event uint64 383 384 func (Uint64Event) AssertEvent() {} 385 386 type StringEvent string 387 388 func (StringEvent) AssertEvent() {}