github.com/defanghe/fabric@v2.1.1+incompatible/gossip/comm/demux_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package comm_test 8 9 import ( 10 "os" 11 "runtime/pprof" 12 "testing" 13 "time" 14 15 "github.com/hyperledger/fabric/gossip/comm" 16 ) 17 18 var matchAnything = func(_ interface{}) bool { return true } 19 20 // fill two channels. 21 func TestChannelDeMultiplexer_EvenOddChannels(t *testing.T) { 22 demux := comm.NewChannelDemultiplexer() 23 evens := demux.AddChannel(func(number interface{}) bool { 24 if i, ok := number.(int); ok { 25 return i%2 == 0 26 } 27 return false 28 }) 29 30 odds := demux.AddChannel(func(number interface{}) bool { 31 if i, ok := number.(int); ok { 32 return i%2 == 1 33 } 34 return false 35 }) 36 demux.DeMultiplex("msg") // neither even, nor odd 37 for i := 0; i < 20; i++ { 38 demux.DeMultiplex(i) 39 } 40 if len(evens) != 10 || len(odds) != 10 { 41 t.Fatalf("filter didn't work, or something got pulled out of the channel buffers, evens:%d, odds:%d", len(evens), len(odds)) 42 } 43 demux.Close() 44 demux.Close() // Close is idempotent 45 // currently existing channels still work 46 for number := range odds { 47 if i, ok := number.(int); ok { 48 if i%2 != 1 { 49 t.Fatal("got an even in my odds") 50 } 51 } 52 } 53 for number := range evens { 54 if i, ok := number.(int); ok { 55 if i%2 != 0 { 56 t.Fatal("got an odd in my evens") 57 } 58 } 59 } 60 61 ensureClosedEmptyChannelWithNilReturn(t, evens) 62 ensureClosedEmptyChannelWithNilReturn(t, odds) 63 } 64 65 func TestChannelDeMultiplexer_OperationsAfterClose(t *testing.T) { 66 demux := comm.NewChannelDemultiplexer() 67 demux.Close() 68 ch := make(chan struct{}) 69 matcher := func(_ interface{}) bool { ch <- struct{}{}; return true } 70 things := demux.AddChannel(matcher) 71 // We should get a closed channel 72 ensureClosedEmptyChannelWithNilReturn(t, things) 73 // demux is closed, so this should exit without attempting a match. 74 demux.DeMultiplex("msg") 75 ensureClosedEmptyChannelWithNilReturn(t, things) 76 select { 77 case <-ch: 78 t.Fatal("matcher should not have been called") 79 default: 80 } 81 } 82 83 func TestChannelDeMultiplexer_ShouldCloseWithFullChannel(t *testing.T) { 84 demux := comm.NewChannelDemultiplexer() 85 demux.AddChannel(matchAnything) 86 for i := 0; i < 10; i++ { 87 demux.DeMultiplex(i) 88 } 89 90 // there is no guarantee that the goroutine runs before 91 // Close() is called, so if this were to fail, it would be 92 // arbitrarily based on the scheduler 93 finished := make(chan struct{}) 94 go func() { 95 demux.DeMultiplex(0) // filled channel, but in the background 96 close(finished) 97 }() 98 demux.Close() // does not hang 99 select { 100 case <-finished: // this forces the goroutine to run to completion 101 case <-time.After(time.Second): 102 t.Fatal("DeMultiplex should return when demux.Close() is called") 103 } 104 } 105 106 // run the operations in various orders to make sure there are no 107 // missing unlocks that would block anything 108 func TestChannelDeMultiplexer_InterleaveOperations(t *testing.T) { 109 finished := make(chan struct{}) 110 go func() { 111 demux := comm.NewChannelDemultiplexer() 112 demux.AddChannel(matchAnything) 113 demux.DeMultiplex("msg") 114 demux.AddChannel(matchAnything) 115 demux.DeMultiplex("msg") 116 demux.Close() 117 demux.AddChannel(matchAnything) 118 demux.DeMultiplex("msg") 119 demux.AddChannel(matchAnything) 120 demux.DeMultiplex("msg") 121 demux.Close() 122 demux.AddChannel(matchAnything) 123 demux.DeMultiplex("msg") 124 demux.AddChannel(matchAnything) 125 demux.DeMultiplex("msg") 126 demux.Close() 127 close(finished) 128 }() 129 130 select { 131 case <-time.After(time.Second): 132 pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) 133 t.Fatal("timed out waiting for operations to complete, may be deadlock") 134 case <-finished: 135 } 136 } 137 138 func ensureClosedEmptyChannelWithNilReturn(t *testing.T, things <-chan interface{}) { 139 if thing, openChannel := <-things; openChannel || thing != nil { 140 t.Fatalf("channel should be closed and should not get non-nil from closed empty channel") 141 } 142 }