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 }