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() {}