github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/libs/events/events_test.go (about)

     1  package events
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"math/rand"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/fortytw2/leaktest"
    11  	"github.com/stretchr/testify/require"
    12  )
    13  
    14  // TestAddListenerForEventFireOnce sets up an EventSwitch, subscribes a single
    15  // listener to an event, and sends a string "data".
    16  func TestAddListenerForEventFireOnce(t *testing.T) {
    17  	ctx, cancel := context.WithCancel(context.Background())
    18  	defer cancel()
    19  
    20  	evsw := NewEventSwitch()
    21  
    22  	messages := make(chan EventData)
    23  	require.NoError(t, evsw.AddListenerForEvent("listener", "event",
    24  		func(data EventData) error {
    25  			select {
    26  			case messages <- data:
    27  				return nil
    28  			case <-ctx.Done():
    29  				return ctx.Err()
    30  			}
    31  		}))
    32  	go evsw.FireEvent("event", "data")
    33  	received := <-messages
    34  	if received != "data" {
    35  		t.Errorf("message received does not match: %v", received)
    36  	}
    37  }
    38  
    39  // TestAddListenerForEventFireMany sets up an EventSwitch, subscribes a single
    40  // listener to an event, and sends a thousand integers.
    41  func TestAddListenerForEventFireMany(t *testing.T) {
    42  	ctx, cancel := context.WithCancel(context.Background())
    43  	defer cancel()
    44  
    45  	evsw := NewEventSwitch()
    46  
    47  	doneSum := make(chan uint64)
    48  	doneSending := make(chan uint64)
    49  	numbers := make(chan uint64, 4)
    50  	// subscribe one listener for one event
    51  	require.NoError(t, evsw.AddListenerForEvent("listener", "event",
    52  		func(data EventData) error {
    53  			select {
    54  			case numbers <- data.(uint64):
    55  				return nil
    56  			case <-ctx.Done():
    57  				return ctx.Err()
    58  			}
    59  		}))
    60  	// collect received events
    61  	go sumReceivedNumbers(numbers, doneSum)
    62  	// go fire events
    63  	go fireEvents(ctx, evsw, "event", doneSending, uint64(1))
    64  	checkSum := <-doneSending
    65  	close(numbers)
    66  	eventSum := <-doneSum
    67  	if checkSum != eventSum {
    68  		t.Errorf("not all messages sent were received.\n")
    69  	}
    70  }
    71  
    72  // TestAddListenerForDifferentEvents sets up an EventSwitch, subscribes a single
    73  // listener to three different events and sends a thousand integers for each
    74  // of the three events.
    75  func TestAddListenerForDifferentEvents(t *testing.T) {
    76  	ctx, cancel := context.WithCancel(context.Background())
    77  	defer cancel()
    78  
    79  	t.Cleanup(leaktest.Check(t))
    80  
    81  	evsw := NewEventSwitch()
    82  
    83  	doneSum := make(chan uint64)
    84  	doneSending1 := make(chan uint64)
    85  	doneSending2 := make(chan uint64)
    86  	doneSending3 := make(chan uint64)
    87  	numbers := make(chan uint64, 4)
    88  	// subscribe one listener to three events
    89  	require.NoError(t, evsw.AddListenerForEvent("listener", "event1",
    90  		func(data EventData) error {
    91  			select {
    92  			case numbers <- data.(uint64):
    93  				return nil
    94  			case <-ctx.Done():
    95  				return ctx.Err()
    96  			}
    97  		}))
    98  	require.NoError(t, evsw.AddListenerForEvent("listener", "event2",
    99  		func(data EventData) error {
   100  			select {
   101  			case numbers <- data.(uint64):
   102  				return nil
   103  			case <-ctx.Done():
   104  				return ctx.Err()
   105  			}
   106  		}))
   107  	require.NoError(t, evsw.AddListenerForEvent("listener", "event3",
   108  		func(data EventData) error {
   109  			select {
   110  			case numbers <- data.(uint64):
   111  				return nil
   112  			case <-ctx.Done():
   113  				return ctx.Err()
   114  			}
   115  		}))
   116  	// collect received events
   117  	go sumReceivedNumbers(numbers, doneSum)
   118  	// go fire events
   119  	go fireEvents(ctx, evsw, "event1", doneSending1, uint64(1))
   120  	go fireEvents(ctx, evsw, "event2", doneSending2, uint64(1))
   121  	go fireEvents(ctx, evsw, "event3", doneSending3, uint64(1))
   122  	var checkSum uint64
   123  	checkSum += <-doneSending1
   124  	checkSum += <-doneSending2
   125  	checkSum += <-doneSending3
   126  	close(numbers)
   127  	eventSum := <-doneSum
   128  	if checkSum != eventSum {
   129  		t.Errorf("not all messages sent were received.\n")
   130  	}
   131  }
   132  
   133  // TestAddDifferentListenerForDifferentEvents sets up an EventSwitch,
   134  // subscribes a first listener to three events, and subscribes a second
   135  // listener to two of those three events, and then sends a thousand integers
   136  // for each of the three events.
   137  func TestAddDifferentListenerForDifferentEvents(t *testing.T) {
   138  	ctx, cancel := context.WithCancel(context.Background())
   139  	defer cancel()
   140  
   141  	t.Cleanup(leaktest.Check(t))
   142  
   143  	evsw := NewEventSwitch()
   144  
   145  	doneSum1 := make(chan uint64)
   146  	doneSum2 := make(chan uint64)
   147  	doneSending1 := make(chan uint64)
   148  	doneSending2 := make(chan uint64)
   149  	doneSending3 := make(chan uint64)
   150  	numbers1 := make(chan uint64, 4)
   151  	numbers2 := make(chan uint64, 4)
   152  	// subscribe two listener to three events
   153  	require.NoError(t, evsw.AddListenerForEvent("listener1", "event1",
   154  		func(data EventData) error {
   155  			select {
   156  			case numbers1 <- data.(uint64):
   157  				return nil
   158  			case <-ctx.Done():
   159  				return ctx.Err()
   160  			}
   161  		}))
   162  	require.NoError(t, evsw.AddListenerForEvent("listener1", "event2",
   163  		func(data EventData) error {
   164  			select {
   165  			case numbers1 <- data.(uint64):
   166  				return nil
   167  			case <-ctx.Done():
   168  				return ctx.Err()
   169  			}
   170  		}))
   171  	require.NoError(t, evsw.AddListenerForEvent("listener1", "event3",
   172  		func(data EventData) error {
   173  			select {
   174  			case numbers1 <- data.(uint64):
   175  				return nil
   176  			case <-ctx.Done():
   177  				return ctx.Err()
   178  			}
   179  		}))
   180  	require.NoError(t, evsw.AddListenerForEvent("listener2", "event2",
   181  		func(data EventData) error {
   182  			select {
   183  			case numbers2 <- data.(uint64):
   184  				return nil
   185  			case <-ctx.Done():
   186  				return ctx.Err()
   187  			}
   188  		}))
   189  	require.NoError(t, evsw.AddListenerForEvent("listener2", "event3",
   190  		func(data EventData) error {
   191  			select {
   192  			case numbers2 <- data.(uint64):
   193  				return nil
   194  			case <-ctx.Done():
   195  				return ctx.Err()
   196  			}
   197  		}))
   198  	// collect received events for listener1
   199  	go sumReceivedNumbers(numbers1, doneSum1)
   200  	// collect received events for listener2
   201  	go sumReceivedNumbers(numbers2, doneSum2)
   202  	// go fire events
   203  	go fireEvents(ctx, evsw, "event1", doneSending1, uint64(1))
   204  	go fireEvents(ctx, evsw, "event2", doneSending2, uint64(1001))
   205  	go fireEvents(ctx, evsw, "event3", doneSending3, uint64(2001))
   206  	checkSumEvent1 := <-doneSending1
   207  	checkSumEvent2 := <-doneSending2
   208  	checkSumEvent3 := <-doneSending3
   209  	checkSum1 := checkSumEvent1 + checkSumEvent2 + checkSumEvent3
   210  	checkSum2 := checkSumEvent2 + checkSumEvent3
   211  	close(numbers1)
   212  	close(numbers2)
   213  	eventSum1 := <-doneSum1
   214  	eventSum2 := <-doneSum2
   215  	if checkSum1 != eventSum1 ||
   216  		checkSum2 != eventSum2 {
   217  		t.Errorf("not all messages sent were received for different listeners to different events.\n")
   218  	}
   219  }
   220  
   221  // TestManagerLiistenersAsync sets up an EventSwitch, subscribes two
   222  // listeners to three events, and fires a thousand integers for each event.
   223  // These two listeners serve as the baseline validation while other listeners
   224  // are randomly subscribed and unsubscribed.
   225  // More precisely it randomly subscribes new listeners (different from the first
   226  // two listeners) to one of these three events. At the same time it starts
   227  // randomly unsubscribing these additional listeners from all events they are
   228  // at that point subscribed to.
   229  // NOTE: it is important to run this test with race conditions tracking on,
   230  // `go test -race`, to examine for possible race conditions.
   231  func TestManageListenersAsync(t *testing.T) {
   232  	ctx, cancel := context.WithCancel(context.Background())
   233  	defer cancel()
   234  
   235  	evsw := NewEventSwitch()
   236  
   237  	doneSum1 := make(chan uint64)
   238  	doneSum2 := make(chan uint64)
   239  	doneSending1 := make(chan uint64)
   240  	doneSending2 := make(chan uint64)
   241  	doneSending3 := make(chan uint64)
   242  	numbers1 := make(chan uint64, 4)
   243  	numbers2 := make(chan uint64, 4)
   244  	// subscribe two listener to three events
   245  	require.NoError(t, evsw.AddListenerForEvent("listener1", "event1",
   246  		func(data EventData) error {
   247  			select {
   248  			case numbers1 <- data.(uint64):
   249  				return nil
   250  			case <-ctx.Done():
   251  				return ctx.Err()
   252  			}
   253  		}))
   254  	require.NoError(t, evsw.AddListenerForEvent("listener1", "event2",
   255  		func(data EventData) error {
   256  			select {
   257  			case numbers1 <- data.(uint64):
   258  				return nil
   259  			case <-ctx.Done():
   260  				return ctx.Err()
   261  			}
   262  		}))
   263  	require.NoError(t, evsw.AddListenerForEvent("listener1", "event3",
   264  		func(data EventData) error {
   265  			select {
   266  			case numbers1 <- data.(uint64):
   267  				return nil
   268  			case <-ctx.Done():
   269  				return ctx.Err()
   270  			}
   271  		}))
   272  	require.NoError(t, evsw.AddListenerForEvent("listener2", "event1",
   273  		func(data EventData) error {
   274  			select {
   275  			case numbers2 <- data.(uint64):
   276  				return nil
   277  			case <-ctx.Done():
   278  				return ctx.Err()
   279  			}
   280  		}))
   281  	require.NoError(t, evsw.AddListenerForEvent("listener2", "event2",
   282  		func(data EventData) error {
   283  			select {
   284  			case numbers2 <- data.(uint64):
   285  				return nil
   286  			case <-ctx.Done():
   287  				return ctx.Err()
   288  			}
   289  		}))
   290  	require.NoError(t, evsw.AddListenerForEvent("listener2", "event3",
   291  		func(data EventData) error {
   292  			select {
   293  			case numbers2 <- data.(uint64):
   294  				return nil
   295  			case <-ctx.Done():
   296  				return ctx.Err()
   297  			}
   298  		}))
   299  	// collect received events for event1
   300  	go sumReceivedNumbers(numbers1, doneSum1)
   301  	// collect received events for event2
   302  	go sumReceivedNumbers(numbers2, doneSum2)
   303  	addListenersStress := func() {
   304  		r1 := rand.New(rand.NewSource(time.Now().Unix()))
   305  		r1.Seed(time.Now().UnixNano())
   306  		for k := uint16(0); k < 400; k++ {
   307  			listenerNumber := r1.Intn(100) + 3
   308  			eventNumber := r1.Intn(3) + 1
   309  			go evsw.AddListenerForEvent(fmt.Sprintf("listener%v", listenerNumber), //nolint:errcheck // ignore for tests
   310  				fmt.Sprintf("event%v", eventNumber),
   311  				func(EventData) error { return nil })
   312  		}
   313  	}
   314  	addListenersStress()
   315  	// go fire events
   316  	go fireEvents(ctx, evsw, "event1", doneSending1, uint64(1))
   317  	go fireEvents(ctx, evsw, "event2", doneSending2, uint64(1001))
   318  	go fireEvents(ctx, evsw, "event3", doneSending3, uint64(2001))
   319  	checkSumEvent1 := <-doneSending1
   320  	checkSumEvent2 := <-doneSending2
   321  	checkSumEvent3 := <-doneSending3
   322  	checkSum := checkSumEvent1 + checkSumEvent2 + checkSumEvent3
   323  	close(numbers1)
   324  	close(numbers2)
   325  	eventSum1 := <-doneSum1
   326  	eventSum2 := <-doneSum2
   327  	if checkSum != eventSum1 ||
   328  		checkSum != eventSum2 {
   329  		t.Errorf("not all messages sent were received.\n")
   330  	}
   331  }
   332  
   333  //------------------------------------------------------------------------------
   334  // Helper functions
   335  
   336  // sumReceivedNumbers takes two channels and adds all numbers received
   337  // until the receiving channel `numbers` is closed; it then sends the sum
   338  // on `doneSum` and closes that channel.  Expected to be run in a go-routine.
   339  func sumReceivedNumbers(numbers, doneSum chan uint64) {
   340  	var sum uint64
   341  	for {
   342  		j, more := <-numbers
   343  		sum += j
   344  		if !more {
   345  			doneSum <- sum
   346  			close(doneSum)
   347  			return
   348  		}
   349  	}
   350  }
   351  
   352  // fireEvents takes an EventSwitch and fires a thousand integers under
   353  // a given `event` with the integers mootonically increasing from `offset`
   354  // to `offset` + 999.  It additionally returns the addition of all integers
   355  // sent on `doneChan` for assertion that all events have been sent, and enabling
   356  // the test to assert all events have also been received.
   357  func fireEvents(ctx context.Context, evsw Fireable, event string, doneChan chan uint64, offset uint64) {
   358  	defer close(doneChan)
   359  
   360  	var sentSum uint64
   361  	for i := offset; i <= offset+uint64(999); i++ {
   362  		if ctx.Err() != nil {
   363  			break
   364  		}
   365  
   366  		evsw.FireEvent(event, i)
   367  		sentSum += i
   368  	}
   369  
   370  	select {
   371  	case <-ctx.Done():
   372  	case doneChan <- sentSum:
   373  	}
   374  }