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  }