bitbucket.org/number571/tendermint@v0.8.14/types/event_bus_test.go (about)

     1  package types
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	mrand "math/rand"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	abci "bitbucket.org/number571/tendermint/abci/types"
    14  	tmpubsub "bitbucket.org/number571/tendermint/libs/pubsub"
    15  	tmquery "bitbucket.org/number571/tendermint/libs/pubsub/query"
    16  )
    17  
    18  func TestEventBusPublishEventTx(t *testing.T) {
    19  	eventBus := NewEventBus()
    20  	err := eventBus.Start()
    21  	require.NoError(t, err)
    22  	t.Cleanup(func() {
    23  		if err := eventBus.Stop(); err != nil {
    24  			t.Error(err)
    25  		}
    26  	})
    27  
    28  	tx := Tx("foo")
    29  	result := abci.ResponseDeliverTx{
    30  		Data: []byte("bar"),
    31  		Events: []abci.Event{
    32  			{Type: "testType", Attributes: []abci.EventAttribute{{Key: "baz", Value: "1"}}},
    33  		},
    34  	}
    35  
    36  	// PublishEventTx adds 3 composite keys, so the query below should work
    37  	query := fmt.Sprintf("tm.event='Tx' AND tx.height=1 AND tx.hash='%X' AND testType.baz=1", tx.Hash())
    38  	txsSub, err := eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query))
    39  	require.NoError(t, err)
    40  
    41  	done := make(chan struct{})
    42  	go func() {
    43  		msg := <-txsSub.Out()
    44  		edt := msg.Data().(EventDataTx)
    45  		assert.Equal(t, int64(1), edt.Height)
    46  		assert.Equal(t, uint32(0), edt.Index)
    47  		assert.EqualValues(t, tx, edt.Tx)
    48  		assert.Equal(t, result, edt.Result)
    49  		close(done)
    50  	}()
    51  
    52  	err = eventBus.PublishEventTx(EventDataTx{abci.TxResult{
    53  		Height: 1,
    54  		Index:  0,
    55  		Tx:     tx,
    56  		Result: result,
    57  	}})
    58  	assert.NoError(t, err)
    59  
    60  	select {
    61  	case <-done:
    62  	case <-time.After(1 * time.Second):
    63  		t.Fatal("did not receive a transaction after 1 sec.")
    64  	}
    65  }
    66  
    67  func TestEventBusPublishEventNewBlock(t *testing.T) {
    68  	eventBus := NewEventBus()
    69  	err := eventBus.Start()
    70  	require.NoError(t, err)
    71  	t.Cleanup(func() {
    72  		if err := eventBus.Stop(); err != nil {
    73  			t.Error(err)
    74  		}
    75  	})
    76  
    77  	block := MakeBlock(0, []Tx{}, nil, []Evidence{})
    78  	blockID := BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(BlockPartSizeBytes).Header()}
    79  	resultBeginBlock := abci.ResponseBeginBlock{
    80  		Events: []abci.Event{
    81  			{Type: "testType", Attributes: []abci.EventAttribute{{Key: "baz", Value: "1"}}},
    82  		},
    83  	}
    84  	resultEndBlock := abci.ResponseEndBlock{
    85  		Events: []abci.Event{
    86  			{Type: "testType", Attributes: []abci.EventAttribute{{Key: "foz", Value: "2"}}},
    87  		},
    88  	}
    89  
    90  	// PublishEventNewBlock adds the tm.event compositeKey, so the query below should work
    91  	query := "tm.event='NewBlock' AND testType.baz=1 AND testType.foz=2"
    92  	blocksSub, err := eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query))
    93  	require.NoError(t, err)
    94  
    95  	done := make(chan struct{})
    96  	go func() {
    97  		msg := <-blocksSub.Out()
    98  		edt := msg.Data().(EventDataNewBlock)
    99  		assert.Equal(t, block, edt.Block)
   100  		assert.Equal(t, blockID, edt.BlockID)
   101  		assert.Equal(t, resultBeginBlock, edt.ResultBeginBlock)
   102  		assert.Equal(t, resultEndBlock, edt.ResultEndBlock)
   103  		close(done)
   104  	}()
   105  
   106  	err = eventBus.PublishEventNewBlock(EventDataNewBlock{
   107  		Block:            block,
   108  		BlockID:          blockID,
   109  		ResultBeginBlock: resultBeginBlock,
   110  		ResultEndBlock:   resultEndBlock,
   111  	})
   112  	assert.NoError(t, err)
   113  
   114  	select {
   115  	case <-done:
   116  	case <-time.After(1 * time.Second):
   117  		t.Fatal("did not receive a block after 1 sec.")
   118  	}
   119  }
   120  
   121  func TestEventBusPublishEventTxDuplicateKeys(t *testing.T) {
   122  	eventBus := NewEventBus()
   123  	err := eventBus.Start()
   124  	require.NoError(t, err)
   125  	t.Cleanup(func() {
   126  		if err := eventBus.Stop(); err != nil {
   127  			t.Error(err)
   128  		}
   129  	})
   130  
   131  	tx := Tx("foo")
   132  	result := abci.ResponseDeliverTx{
   133  		Data: []byte("bar"),
   134  		Events: []abci.Event{
   135  			{
   136  				Type: "transfer",
   137  				Attributes: []abci.EventAttribute{
   138  					{Key: "sender", Value: "foo"},
   139  					{Key: "recipient", Value: "bar"},
   140  					{Key: "amount", Value: "5"},
   141  				},
   142  			},
   143  			{
   144  				Type: "transfer",
   145  				Attributes: []abci.EventAttribute{
   146  					{Key: "sender", Value: "baz"},
   147  					{Key: "recipient", Value: "cat"},
   148  					{Key: "amount", Value: "13"},
   149  				},
   150  			},
   151  			{
   152  				Type: "withdraw.rewards",
   153  				Attributes: []abci.EventAttribute{
   154  					{Key: "address", Value: "bar"},
   155  					{Key: "source", Value: "iceman"},
   156  					{Key: "amount", Value: "33"},
   157  				},
   158  			},
   159  		},
   160  	}
   161  
   162  	testCases := []struct {
   163  		query         string
   164  		expectResults bool
   165  	}{
   166  		{
   167  			"tm.event='Tx' AND tx.height=1 AND transfer.sender='DoesNotExist'",
   168  			false,
   169  		},
   170  		{
   171  			"tm.event='Tx' AND tx.height=1 AND transfer.sender='foo'",
   172  			true,
   173  		},
   174  		{
   175  			"tm.event='Tx' AND tx.height=1 AND transfer.sender='baz'",
   176  			true,
   177  		},
   178  		{
   179  			"tm.event='Tx' AND tx.height=1 AND transfer.sender='foo' AND transfer.sender='baz'",
   180  			true,
   181  		},
   182  		{
   183  			"tm.event='Tx' AND tx.height=1 AND transfer.sender='foo' AND transfer.sender='DoesNotExist'",
   184  			false,
   185  		},
   186  	}
   187  
   188  	for i, tc := range testCases {
   189  		sub, err := eventBus.Subscribe(context.Background(), fmt.Sprintf("client-%d", i), tmquery.MustParse(tc.query))
   190  		require.NoError(t, err)
   191  
   192  		done := make(chan struct{})
   193  
   194  		go func() {
   195  			select {
   196  			case msg := <-sub.Out():
   197  				data := msg.Data().(EventDataTx)
   198  				assert.Equal(t, int64(1), data.Height)
   199  				assert.Equal(t, uint32(0), data.Index)
   200  				assert.EqualValues(t, tx, data.Tx)
   201  				assert.Equal(t, result, data.Result)
   202  				close(done)
   203  			case <-time.After(1 * time.Second):
   204  				return
   205  			}
   206  		}()
   207  
   208  		err = eventBus.PublishEventTx(EventDataTx{abci.TxResult{
   209  			Height: 1,
   210  			Index:  0,
   211  			Tx:     tx,
   212  			Result: result,
   213  		}})
   214  		assert.NoError(t, err)
   215  
   216  		select {
   217  		case <-done:
   218  			if !tc.expectResults {
   219  				require.Fail(t, "unexpected transaction result(s) from subscription")
   220  			}
   221  		case <-time.After(1 * time.Second):
   222  			if tc.expectResults {
   223  				require.Fail(t, "failed to receive a transaction after 1 second")
   224  			}
   225  		}
   226  	}
   227  }
   228  
   229  func TestEventBusPublishEventNewBlockHeader(t *testing.T) {
   230  	eventBus := NewEventBus()
   231  	err := eventBus.Start()
   232  	require.NoError(t, err)
   233  	t.Cleanup(func() {
   234  		if err := eventBus.Stop(); err != nil {
   235  			t.Error(err)
   236  		}
   237  	})
   238  
   239  	block := MakeBlock(0, []Tx{}, nil, []Evidence{})
   240  	resultBeginBlock := abci.ResponseBeginBlock{
   241  		Events: []abci.Event{
   242  			{Type: "testType", Attributes: []abci.EventAttribute{{Key: "baz", Value: "1"}}},
   243  		},
   244  	}
   245  	resultEndBlock := abci.ResponseEndBlock{
   246  		Events: []abci.Event{
   247  			{Type: "testType", Attributes: []abci.EventAttribute{{Key: "foz", Value: "2"}}},
   248  		},
   249  	}
   250  
   251  	// PublishEventNewBlockHeader adds the tm.event compositeKey, so the query below should work
   252  	query := "tm.event='NewBlockHeader' AND testType.baz=1 AND testType.foz=2"
   253  	headersSub, err := eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query))
   254  	require.NoError(t, err)
   255  
   256  	done := make(chan struct{})
   257  	go func() {
   258  		msg := <-headersSub.Out()
   259  		edt := msg.Data().(EventDataNewBlockHeader)
   260  		assert.Equal(t, block.Header, edt.Header)
   261  		assert.Equal(t, resultBeginBlock, edt.ResultBeginBlock)
   262  		assert.Equal(t, resultEndBlock, edt.ResultEndBlock)
   263  		close(done)
   264  	}()
   265  
   266  	err = eventBus.PublishEventNewBlockHeader(EventDataNewBlockHeader{
   267  		Header:           block.Header,
   268  		ResultBeginBlock: resultBeginBlock,
   269  		ResultEndBlock:   resultEndBlock,
   270  	})
   271  	assert.NoError(t, err)
   272  
   273  	select {
   274  	case <-done:
   275  	case <-time.After(1 * time.Second):
   276  		t.Fatal("did not receive a block header after 1 sec.")
   277  	}
   278  }
   279  
   280  func TestEventBusPublishEventNewEvidence(t *testing.T) {
   281  	eventBus := NewEventBus()
   282  	err := eventBus.Start()
   283  	require.NoError(t, err)
   284  	t.Cleanup(func() {
   285  		if err := eventBus.Stop(); err != nil {
   286  			t.Error(err)
   287  		}
   288  	})
   289  
   290  	ev := NewMockDuplicateVoteEvidence(1, time.Now(), "test-chain-id")
   291  
   292  	query := "tm.event='NewEvidence'"
   293  	evSub, err := eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query))
   294  	require.NoError(t, err)
   295  
   296  	done := make(chan struct{})
   297  	go func() {
   298  		msg := <-evSub.Out()
   299  		edt := msg.Data().(EventDataNewEvidence)
   300  		assert.Equal(t, ev, edt.Evidence)
   301  		assert.Equal(t, int64(4), edt.Height)
   302  		close(done)
   303  	}()
   304  
   305  	err = eventBus.PublishEventNewEvidence(EventDataNewEvidence{
   306  		Evidence: ev,
   307  		Height:   4,
   308  	})
   309  	assert.NoError(t, err)
   310  
   311  	select {
   312  	case <-done:
   313  	case <-time.After(1 * time.Second):
   314  		t.Fatal("did not receive a block header after 1 sec.")
   315  	}
   316  }
   317  
   318  func TestEventBusPublish(t *testing.T) {
   319  	eventBus := NewEventBus()
   320  	err := eventBus.Start()
   321  	require.NoError(t, err)
   322  	t.Cleanup(func() {
   323  		if err := eventBus.Stop(); err != nil {
   324  			t.Error(err)
   325  		}
   326  	})
   327  
   328  	const numEventsExpected = 14
   329  
   330  	sub, err := eventBus.Subscribe(context.Background(), "test", tmquery.Empty{}, numEventsExpected)
   331  	require.NoError(t, err)
   332  
   333  	done := make(chan struct{})
   334  	go func() {
   335  		numEvents := 0
   336  		for range sub.Out() {
   337  			numEvents++
   338  			if numEvents >= numEventsExpected {
   339  				close(done)
   340  				return
   341  			}
   342  		}
   343  	}()
   344  
   345  	err = eventBus.Publish(EventNewBlockHeaderValue, EventDataNewBlockHeader{})
   346  	require.NoError(t, err)
   347  	err = eventBus.PublishEventNewBlock(EventDataNewBlock{})
   348  	require.NoError(t, err)
   349  	err = eventBus.PublishEventNewBlockHeader(EventDataNewBlockHeader{})
   350  	require.NoError(t, err)
   351  	err = eventBus.PublishEventVote(EventDataVote{})
   352  	require.NoError(t, err)
   353  	err = eventBus.PublishEventNewRoundStep(EventDataRoundState{})
   354  	require.NoError(t, err)
   355  	err = eventBus.PublishEventTimeoutPropose(EventDataRoundState{})
   356  	require.NoError(t, err)
   357  	err = eventBus.PublishEventTimeoutWait(EventDataRoundState{})
   358  	require.NoError(t, err)
   359  	err = eventBus.PublishEventNewRound(EventDataNewRound{})
   360  	require.NoError(t, err)
   361  	err = eventBus.PublishEventCompleteProposal(EventDataCompleteProposal{})
   362  	require.NoError(t, err)
   363  	err = eventBus.PublishEventPolka(EventDataRoundState{})
   364  	require.NoError(t, err)
   365  	err = eventBus.PublishEventUnlock(EventDataRoundState{})
   366  	require.NoError(t, err)
   367  	err = eventBus.PublishEventRelock(EventDataRoundState{})
   368  	require.NoError(t, err)
   369  	err = eventBus.PublishEventLock(EventDataRoundState{})
   370  	require.NoError(t, err)
   371  	err = eventBus.PublishEventValidatorSetUpdates(EventDataValidatorSetUpdates{})
   372  	require.NoError(t, err)
   373  	err = eventBus.PublishEventFastSyncStatus(EventDataFastSyncStatus{})
   374  	require.NoError(t, err)
   375  	err = eventBus.PublishEventStateSyncStatus(EventDataStateSyncStatus{})
   376  	require.NoError(t, err)
   377  
   378  	select {
   379  	case <-done:
   380  	case <-time.After(1 * time.Second):
   381  		t.Fatalf("expected to receive %d events after 1 sec.", numEventsExpected)
   382  	}
   383  }
   384  
   385  func BenchmarkEventBus(b *testing.B) {
   386  	benchmarks := []struct {
   387  		name        string
   388  		numClients  int
   389  		randQueries bool
   390  		randEvents  bool
   391  	}{
   392  		{"10Clients1Query1Event", 10, false, false},
   393  		{"100Clients", 100, false, false},
   394  		{"1000Clients", 1000, false, false},
   395  
   396  		{"10ClientsRandQueries1Event", 10, true, false},
   397  		{"100Clients", 100, true, false},
   398  		{"1000Clients", 1000, true, false},
   399  
   400  		{"10ClientsRandQueriesRandEvents", 10, true, true},
   401  		{"100Clients", 100, true, true},
   402  		{"1000Clients", 1000, true, true},
   403  
   404  		{"10Clients1QueryRandEvents", 10, false, true},
   405  		{"100Clients", 100, false, true},
   406  		{"1000Clients", 1000, false, true},
   407  	}
   408  
   409  	for _, bm := range benchmarks {
   410  		bm := bm
   411  		b.Run(bm.name, func(b *testing.B) {
   412  			benchmarkEventBus(bm.numClients, bm.randQueries, bm.randEvents, b)
   413  		})
   414  	}
   415  }
   416  
   417  func benchmarkEventBus(numClients int, randQueries bool, randEvents bool, b *testing.B) {
   418  	// for random* functions
   419  	mrand.Seed(time.Now().Unix())
   420  
   421  	eventBus := NewEventBusWithBufferCapacity(0) // set buffer capacity to 0 so we are not testing cache
   422  	err := eventBus.Start()
   423  	if err != nil {
   424  		b.Error(err)
   425  	}
   426  	b.Cleanup(func() {
   427  		if err := eventBus.Stop(); err != nil {
   428  			b.Error(err)
   429  		}
   430  	})
   431  
   432  	ctx := context.Background()
   433  	q := EventQueryNewBlock
   434  
   435  	for i := 0; i < numClients; i++ {
   436  		if randQueries {
   437  			q = randQuery()
   438  		}
   439  		sub, err := eventBus.Subscribe(ctx, fmt.Sprintf("client-%d", i), q)
   440  		if err != nil {
   441  			b.Fatal(err)
   442  		}
   443  		go func() {
   444  			for {
   445  				select {
   446  				case <-sub.Out():
   447  				case <-sub.Canceled():
   448  					return
   449  				}
   450  			}
   451  		}()
   452  	}
   453  
   454  	eventValue := EventNewBlockValue
   455  
   456  	b.ReportAllocs()
   457  	b.ResetTimer()
   458  	for i := 0; i < b.N; i++ {
   459  		if randEvents {
   460  			eventValue = randEventValue()
   461  		}
   462  
   463  		err := eventBus.Publish(eventValue, EventDataString("Gamora"))
   464  		if err != nil {
   465  			b.Error(err)
   466  		}
   467  	}
   468  }
   469  
   470  var events = []string{
   471  	EventNewBlockValue,
   472  	EventNewBlockHeaderValue,
   473  	EventNewRoundValue,
   474  	EventNewRoundStepValue,
   475  	EventTimeoutProposeValue,
   476  	EventCompleteProposalValue,
   477  	EventPolkaValue,
   478  	EventUnlockValue,
   479  	EventLockValue,
   480  	EventRelockValue,
   481  	EventTimeoutWaitValue,
   482  	EventVoteValue,
   483  	EventFastSyncStatusValue,
   484  	EventStateSyncStatusValue,
   485  }
   486  
   487  func randEventValue() string {
   488  
   489  	return events[mrand.Intn(len(events))]
   490  }
   491  
   492  var queries = []tmpubsub.Query{
   493  	EventQueryNewBlock,
   494  	EventQueryNewBlockHeader,
   495  	EventQueryNewRound,
   496  	EventQueryNewRoundStep,
   497  	EventQueryTimeoutPropose,
   498  	EventQueryCompleteProposal,
   499  	EventQueryPolka,
   500  	EventQueryUnlock,
   501  	EventQueryLock,
   502  	EventQueryRelock,
   503  	EventQueryTimeoutWait,
   504  	EventQueryVote,
   505  	EventQueryFastSyncStatus}
   506  
   507  func randQuery() tmpubsub.Query {
   508  	return queries[mrand.Intn(len(queries))]
   509  }