github.com/line/ostracon@v1.0.10-0.20230328032236-7f20145f065d/p2p/conn/connection_test.go (about)

     1  package conn
     2  
     3  import (
     4  	"encoding/hex"
     5  	"net"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/fortytw2/leaktest"
    10  	"github.com/gogo/protobuf/proto"
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  
    14  	tmp2p "github.com/tendermint/tendermint/proto/tendermint/p2p"
    15  	"github.com/tendermint/tendermint/proto/tendermint/types"
    16  
    17  	"github.com/line/ostracon/libs/log"
    18  	"github.com/line/ostracon/libs/protoio"
    19  )
    20  
    21  const maxPingPongPacketSize = 1024 // bytes
    22  
    23  func createTestMConnection(conn net.Conn) *MConnection {
    24  	onReceive := func(chID byte, msgBytes []byte) {
    25  	}
    26  	onError := func(r interface{}) {
    27  	}
    28  	c := createMConnectionWithCallbacks(conn, onReceive, onError)
    29  	c.SetLogger(log.TestingLogger())
    30  	return c
    31  }
    32  
    33  func createMConnectionWithCallbacks(
    34  	conn net.Conn,
    35  	onReceive func(chID byte, msgBytes []byte),
    36  	onError func(r interface{}),
    37  ) *MConnection {
    38  	cfg := DefaultMConnConfig()
    39  	cfg.PingInterval = 90 * time.Millisecond
    40  	cfg.PongTimeout = 45 * time.Millisecond
    41  	chDescs := []*ChannelDescriptor{{ID: 0x01, Priority: 1, SendQueueCapacity: 1}}
    42  	c := NewMConnectionWithConfig(conn, chDescs, onReceive, onError, cfg)
    43  	c.SetLogger(log.TestingLogger())
    44  	return c
    45  }
    46  
    47  func TestMConnectionSendFlushStop(t *testing.T) {
    48  	server, client := NetPipe()
    49  	defer server.Close()
    50  	defer client.Close()
    51  
    52  	clientConn := createTestMConnection(client)
    53  	err := clientConn.Start()
    54  	require.Nil(t, err)
    55  	defer clientConn.Stop() // nolint:errcheck // ignore for tests
    56  
    57  	msg := []byte("abc")
    58  	assert.True(t, clientConn.Send(0x01, msg))
    59  
    60  	msgLength := 14
    61  
    62  	// start the reader in a new routine, so we can flush
    63  	errCh := make(chan error)
    64  	go func() {
    65  		msgB := make([]byte, msgLength)
    66  		_, err := server.Read(msgB)
    67  		if err != nil {
    68  			t.Error(err)
    69  			return
    70  		}
    71  		errCh <- err
    72  	}()
    73  
    74  	// stop the conn - it should flush all conns
    75  	clientConn.FlushStop()
    76  
    77  	timer := time.NewTimer(3 * time.Second)
    78  	select {
    79  	case <-errCh:
    80  	case <-timer.C:
    81  		t.Error("timed out waiting for msgs to be read")
    82  	}
    83  }
    84  
    85  func TestMConnectionSend(t *testing.T) {
    86  	server, client := NetPipe()
    87  	defer server.Close()
    88  	defer client.Close()
    89  
    90  	mconn := createTestMConnection(client)
    91  	err := mconn.Start()
    92  	require.Nil(t, err)
    93  	defer mconn.Stop() // nolint:errcheck // ignore for tests
    94  
    95  	msg := []byte("Ant-Man")
    96  	assert.True(t, mconn.Send(0x01, msg))
    97  	// Note: subsequent Send/TrySend calls could pass because we are reading from
    98  	// the send queue in a separate goroutine.
    99  	_, err = server.Read(make([]byte, len(msg)))
   100  	if err != nil {
   101  		t.Error(err)
   102  	}
   103  	assert.True(t, mconn.CanSend(0x01))
   104  
   105  	msg = []byte("Spider-Man")
   106  	assert.True(t, mconn.TrySend(0x01, msg))
   107  	_, err = server.Read(make([]byte, len(msg)))
   108  	if err != nil {
   109  		t.Error(err)
   110  	}
   111  
   112  	assert.False(t, mconn.CanSend(0x05), "CanSend should return false because channel is unknown")
   113  	assert.False(t, mconn.Send(0x05, []byte("Absorbing Man")), "Send should return false because channel is unknown")
   114  }
   115  
   116  func TestMConnectionReceive(t *testing.T) {
   117  	server, client := NetPipe()
   118  	defer server.Close()
   119  	defer client.Close()
   120  
   121  	receivedCh := make(chan []byte)
   122  	errorsCh := make(chan interface{})
   123  	onReceive := func(chID byte, msgBytes []byte) {
   124  		receivedCh <- msgBytes
   125  	}
   126  	onError := func(r interface{}) {
   127  		errorsCh <- r
   128  	}
   129  	mconn1 := createMConnectionWithCallbacks(client, onReceive, onError)
   130  	err := mconn1.Start()
   131  	require.Nil(t, err)
   132  	defer mconn1.Stop() // nolint:errcheck // ignore for tests
   133  
   134  	mconn2 := createTestMConnection(server)
   135  	err = mconn2.Start()
   136  	require.Nil(t, err)
   137  	defer mconn2.Stop() // nolint:errcheck // ignore for tests
   138  
   139  	msg := []byte("Cyclops")
   140  	assert.True(t, mconn2.Send(0x01, msg))
   141  
   142  	select {
   143  	case receivedBytes := <-receivedCh:
   144  		assert.Equal(t, msg, receivedBytes)
   145  	case err := <-errorsCh:
   146  		t.Fatalf("Expected %s, got %+v", msg, err)
   147  	case <-time.After(500 * time.Millisecond):
   148  		t.Fatalf("Did not receive %s message in 500ms", msg)
   149  	}
   150  }
   151  
   152  func TestMConnectionStatus(t *testing.T) {
   153  	server, client := NetPipe()
   154  	defer server.Close()
   155  	defer client.Close()
   156  
   157  	mconn := createTestMConnection(client)
   158  	err := mconn.Start()
   159  	require.Nil(t, err)
   160  	defer mconn.Stop() // nolint:errcheck // ignore for tests
   161  
   162  	status := mconn.Status()
   163  	assert.NotNil(t, status)
   164  	assert.Zero(t, status.Channels[0].SendQueueSize)
   165  }
   166  
   167  func TestMConnectionPongTimeoutResultsInError(t *testing.T) {
   168  	server, client := net.Pipe()
   169  	defer server.Close()
   170  	defer client.Close()
   171  
   172  	receivedCh := make(chan []byte)
   173  	errorsCh := make(chan interface{})
   174  	onReceive := func(chID byte, msgBytes []byte) {
   175  		receivedCh <- msgBytes
   176  	}
   177  	onError := func(r interface{}) {
   178  		errorsCh <- r
   179  	}
   180  	mconn := createMConnectionWithCallbacks(client, onReceive, onError)
   181  	err := mconn.Start()
   182  	require.Nil(t, err)
   183  	defer mconn.Stop() // nolint:errcheck // ignore for tests
   184  
   185  	serverGotPing := make(chan struct{})
   186  	go func() {
   187  		// read ping
   188  		var pkt tmp2p.Packet
   189  		_, err := protoio.NewDelimitedReader(server, maxPingPongPacketSize).ReadMsg(&pkt)
   190  		require.NoError(t, err)
   191  		serverGotPing <- struct{}{}
   192  	}()
   193  	<-serverGotPing
   194  
   195  	pongTimerExpired := mconn.config.PongTimeout + 200*time.Millisecond
   196  	select {
   197  	case msgBytes := <-receivedCh:
   198  		t.Fatalf("Expected error, but got %v", msgBytes)
   199  	case err := <-errorsCh:
   200  		assert.NotNil(t, err)
   201  	case <-time.After(pongTimerExpired):
   202  		t.Fatalf("Expected to receive error after %v", pongTimerExpired)
   203  	}
   204  }
   205  
   206  func TestMConnectionMultiplePongsInTheBeginning(t *testing.T) {
   207  	server, client := net.Pipe()
   208  	defer server.Close()
   209  	defer client.Close()
   210  
   211  	receivedCh := make(chan []byte)
   212  	errorsCh := make(chan interface{})
   213  	onReceive := func(chID byte, msgBytes []byte) {
   214  		receivedCh <- msgBytes
   215  	}
   216  	onError := func(r interface{}) {
   217  		errorsCh <- r
   218  	}
   219  	mconn := createMConnectionWithCallbacks(client, onReceive, onError)
   220  	err := mconn.Start()
   221  	require.Nil(t, err)
   222  	defer mconn.Stop() // nolint:errcheck // ignore for tests
   223  
   224  	// sending 3 pongs in a row (abuse)
   225  	protoWriter := protoio.NewDelimitedWriter(server)
   226  
   227  	_, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPong{}))
   228  	require.NoError(t, err)
   229  
   230  	_, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPong{}))
   231  	require.NoError(t, err)
   232  
   233  	_, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPong{}))
   234  	require.NoError(t, err)
   235  
   236  	serverGotPing := make(chan struct{})
   237  	go func() {
   238  		// read ping (one byte)
   239  		var packet tmp2p.Packet
   240  		_, err := protoio.NewDelimitedReader(server, maxPingPongPacketSize).ReadMsg(&packet)
   241  		require.NoError(t, err)
   242  		serverGotPing <- struct{}{}
   243  
   244  		// respond with pong
   245  		_, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPong{}))
   246  		require.NoError(t, err)
   247  	}()
   248  	<-serverGotPing
   249  
   250  	pongTimerExpired := mconn.config.PongTimeout + 20*time.Millisecond
   251  	select {
   252  	case msgBytes := <-receivedCh:
   253  		t.Fatalf("Expected no data, but got %v", msgBytes)
   254  	case err := <-errorsCh:
   255  		t.Fatalf("Expected no error, but got %v", err)
   256  	case <-time.After(pongTimerExpired):
   257  		assert.True(t, mconn.IsRunning())
   258  	}
   259  }
   260  
   261  func TestMConnectionMultiplePings(t *testing.T) {
   262  	server, client := net.Pipe()
   263  	defer server.Close()
   264  	defer client.Close()
   265  
   266  	receivedCh := make(chan []byte)
   267  	errorsCh := make(chan interface{})
   268  	onReceive := func(chID byte, msgBytes []byte) {
   269  		receivedCh <- msgBytes
   270  	}
   271  	onError := func(r interface{}) {
   272  		errorsCh <- r
   273  	}
   274  	mconn := createMConnectionWithCallbacks(client, onReceive, onError)
   275  	err := mconn.Start()
   276  	require.Nil(t, err)
   277  	defer mconn.Stop() // nolint:errcheck // ignore for tests
   278  
   279  	// sending 3 pings in a row (abuse)
   280  	// see https://github.com/tendermint/tendermint/issues/1190
   281  	protoReader := protoio.NewDelimitedReader(server, maxPingPongPacketSize)
   282  	protoWriter := protoio.NewDelimitedWriter(server)
   283  	var pkt tmp2p.Packet
   284  
   285  	_, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPing{}))
   286  	require.NoError(t, err)
   287  
   288  	_, err = protoReader.ReadMsg(&pkt)
   289  	require.NoError(t, err)
   290  
   291  	_, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPing{}))
   292  	require.NoError(t, err)
   293  
   294  	_, err = protoReader.ReadMsg(&pkt)
   295  	require.NoError(t, err)
   296  
   297  	_, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPing{}))
   298  	require.NoError(t, err)
   299  
   300  	_, err = protoReader.ReadMsg(&pkt)
   301  	require.NoError(t, err)
   302  
   303  	assert.True(t, mconn.IsRunning())
   304  }
   305  
   306  func TestMConnectionPingPongs(t *testing.T) {
   307  	// check that we are not leaking any go-routines
   308  	defer leaktest.CheckTimeout(t, 10*time.Second)()
   309  
   310  	server, client := net.Pipe()
   311  
   312  	defer server.Close()
   313  	defer client.Close()
   314  
   315  	receivedCh := make(chan []byte)
   316  	errorsCh := make(chan interface{})
   317  	onReceive := func(chID byte, msgBytes []byte) {
   318  		receivedCh <- msgBytes
   319  	}
   320  	onError := func(r interface{}) {
   321  		errorsCh <- r
   322  	}
   323  	mconn := createMConnectionWithCallbacks(client, onReceive, onError)
   324  	err := mconn.Start()
   325  	require.Nil(t, err)
   326  	defer mconn.Stop() // nolint:errcheck // ignore for tests
   327  
   328  	serverGotPing := make(chan struct{})
   329  	go func() {
   330  		protoReader := protoio.NewDelimitedReader(server, maxPingPongPacketSize)
   331  		protoWriter := protoio.NewDelimitedWriter(server)
   332  		var pkt tmp2p.PacketPing
   333  
   334  		// read ping
   335  		_, err = protoReader.ReadMsg(&pkt)
   336  		require.NoError(t, err)
   337  		serverGotPing <- struct{}{}
   338  
   339  		// respond with pong
   340  		_, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPong{}))
   341  		require.NoError(t, err)
   342  
   343  		time.Sleep(mconn.config.PingInterval)
   344  
   345  		// read ping
   346  		_, err = protoReader.ReadMsg(&pkt)
   347  		require.NoError(t, err)
   348  		serverGotPing <- struct{}{}
   349  
   350  		// respond with pong
   351  		_, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPong{}))
   352  		require.NoError(t, err)
   353  	}()
   354  	<-serverGotPing
   355  	<-serverGotPing
   356  
   357  	pongTimerExpired := (mconn.config.PongTimeout + 20*time.Millisecond) * 2
   358  	select {
   359  	case msgBytes := <-receivedCh:
   360  		t.Fatalf("Expected no data, but got %v", msgBytes)
   361  	case err := <-errorsCh:
   362  		t.Fatalf("Expected no error, but got %v", err)
   363  	case <-time.After(2 * pongTimerExpired):
   364  		assert.True(t, mconn.IsRunning())
   365  	}
   366  }
   367  
   368  func TestMConnectionStopsAndReturnsError(t *testing.T) {
   369  	server, client := NetPipe()
   370  	defer server.Close()
   371  	defer client.Close()
   372  
   373  	receivedCh := make(chan []byte)
   374  	errorsCh := make(chan interface{})
   375  	onReceive := func(chID byte, msgBytes []byte) {
   376  		receivedCh <- msgBytes
   377  	}
   378  	onError := func(r interface{}) {
   379  		errorsCh <- r
   380  	}
   381  	mconn := createMConnectionWithCallbacks(client, onReceive, onError)
   382  	err := mconn.Start()
   383  	require.Nil(t, err)
   384  	defer mconn.Stop() // nolint:errcheck // ignore for tests
   385  
   386  	if err := client.Close(); err != nil {
   387  		t.Error(err)
   388  	}
   389  
   390  	select {
   391  	case receivedBytes := <-receivedCh:
   392  		t.Fatalf("Expected error, got %v", receivedBytes)
   393  	case err := <-errorsCh:
   394  		assert.NotNil(t, err)
   395  		assert.False(t, mconn.IsRunning())
   396  	case <-time.After(500 * time.Millisecond):
   397  		t.Fatal("Did not receive error in 500ms")
   398  	}
   399  }
   400  
   401  func newClientAndServerConnsForReadErrors(t *testing.T, chOnErr chan struct{}) (*MConnection, *MConnection) {
   402  	server, client := NetPipe()
   403  
   404  	onReceive := func(chID byte, msgBytes []byte) {}
   405  	onError := func(r interface{}) {}
   406  
   407  	// create client conn with two channels
   408  	chDescs := []*ChannelDescriptor{
   409  		{ID: 0x01, Priority: 1, SendQueueCapacity: 1},
   410  		{ID: 0x02, Priority: 1, SendQueueCapacity: 1},
   411  	}
   412  	mconnClient := NewMConnection(client, chDescs, onReceive, onError)
   413  	mconnClient.SetLogger(log.TestingLogger().With("module", "client"))
   414  	err := mconnClient.Start()
   415  	require.Nil(t, err)
   416  
   417  	// create server conn with 1 channel
   418  	// it fires on chOnErr when there's an error
   419  	serverLogger := log.TestingLogger().With("module", "server")
   420  	onError = func(r interface{}) {
   421  		chOnErr <- struct{}{}
   422  	}
   423  	mconnServer := createMConnectionWithCallbacks(server, onReceive, onError)
   424  	mconnServer.SetLogger(serverLogger)
   425  	err = mconnServer.Start()
   426  	require.Nil(t, err)
   427  	return mconnClient, mconnServer
   428  }
   429  
   430  func expectSend(ch chan struct{}) bool {
   431  	after := time.After(time.Second * 5)
   432  	select {
   433  	case <-ch:
   434  		return true
   435  	case <-after:
   436  		return false
   437  	}
   438  }
   439  
   440  func TestMConnectionReadErrorBadEncoding(t *testing.T) {
   441  	chOnErr := make(chan struct{})
   442  	mconnClient, mconnServer := newClientAndServerConnsForReadErrors(t, chOnErr)
   443  
   444  	client := mconnClient.conn
   445  
   446  	// Write it.
   447  	_, err := client.Write([]byte{1, 2, 3, 4, 5})
   448  	require.NoError(t, err)
   449  	assert.True(t, expectSend(chOnErr), "badly encoded msgPacket")
   450  
   451  	t.Cleanup(func() {
   452  		if err := mconnClient.Stop(); err != nil {
   453  			t.Log(err)
   454  		}
   455  	})
   456  
   457  	t.Cleanup(func() {
   458  		if err := mconnServer.Stop(); err != nil {
   459  			t.Log(err)
   460  		}
   461  	})
   462  }
   463  
   464  func TestMConnectionReadErrorUnknownChannel(t *testing.T) {
   465  	chOnErr := make(chan struct{})
   466  	mconnClient, mconnServer := newClientAndServerConnsForReadErrors(t, chOnErr)
   467  
   468  	msg := []byte("Ant-Man")
   469  
   470  	// fail to send msg on channel unknown by client
   471  	assert.False(t, mconnClient.Send(0x03, msg))
   472  
   473  	// send msg on channel unknown by the server.
   474  	// should cause an error
   475  	assert.True(t, mconnClient.Send(0x02, msg))
   476  	assert.True(t, expectSend(chOnErr), "unknown channel")
   477  
   478  	t.Cleanup(func() {
   479  		if err := mconnClient.Stop(); err != nil {
   480  			t.Log(err)
   481  		}
   482  	})
   483  
   484  	t.Cleanup(func() {
   485  		if err := mconnServer.Stop(); err != nil {
   486  			t.Log(err)
   487  		}
   488  	})
   489  }
   490  
   491  func TestMConnectionReadErrorLongMessage(t *testing.T) {
   492  	chOnErr := make(chan struct{})
   493  	chOnRcv := make(chan struct{})
   494  
   495  	mconnClient, mconnServer := newClientAndServerConnsForReadErrors(t, chOnErr)
   496  	defer mconnClient.Stop() // nolint:errcheck // ignore for tests
   497  	defer mconnServer.Stop() // nolint:errcheck // ignore for tests
   498  
   499  	mconnServer.onReceive = func(chID byte, msgBytes []byte) {
   500  		chOnRcv <- struct{}{}
   501  	}
   502  
   503  	client := mconnClient.conn
   504  	protoWriter := protoio.NewDelimitedWriter(client)
   505  
   506  	// send msg thats just right
   507  	var packet = tmp2p.PacketMsg{
   508  		ChannelID: 0x01,
   509  		EOF:       true,
   510  		Data:      make([]byte, mconnClient.config.MaxPacketMsgPayloadSize),
   511  	}
   512  
   513  	_, err := protoWriter.WriteMsg(mustWrapPacket(&packet))
   514  	require.NoError(t, err)
   515  	assert.True(t, expectSend(chOnRcv), "msg just right")
   516  
   517  	// send msg thats too long
   518  	packet = tmp2p.PacketMsg{
   519  		ChannelID: 0x01,
   520  		EOF:       true,
   521  		Data:      make([]byte, mconnClient.config.MaxPacketMsgPayloadSize+100),
   522  	}
   523  
   524  	_, err = protoWriter.WriteMsg(mustWrapPacket(&packet))
   525  	require.Error(t, err)
   526  	assert.True(t, expectSend(chOnErr), "msg too long")
   527  }
   528  
   529  func TestMConnectionReadErrorUnknownMsgType(t *testing.T) {
   530  	chOnErr := make(chan struct{})
   531  	mconnClient, mconnServer := newClientAndServerConnsForReadErrors(t, chOnErr)
   532  	defer mconnClient.Stop() // nolint:errcheck // ignore for tests
   533  	defer mconnServer.Stop() // nolint:errcheck // ignore for tests
   534  
   535  	// send msg with unknown msg type
   536  	_, err := protoio.NewDelimitedWriter(mconnClient.conn).WriteMsg(&types.Header{ChainID: "x"})
   537  	require.NoError(t, err)
   538  	assert.True(t, expectSend(chOnErr), "unknown msg type")
   539  }
   540  
   541  func TestMConnectionTrySend(t *testing.T) {
   542  	server, client := NetPipe()
   543  	defer server.Close()
   544  	defer client.Close()
   545  
   546  	mconn := createTestMConnection(client)
   547  	err := mconn.Start()
   548  	require.Nil(t, err)
   549  	defer mconn.Stop() // nolint:errcheck // ignore for tests
   550  
   551  	msg := []byte("Semicolon-Woman")
   552  	resultCh := make(chan string, 2)
   553  	assert.True(t, mconn.TrySend(0x01, msg))
   554  	_, err = server.Read(make([]byte, len(msg)))
   555  	require.NoError(t, err)
   556  	assert.True(t, mconn.CanSend(0x01))
   557  	assert.True(t, mconn.TrySend(0x01, msg))
   558  	assert.False(t, mconn.CanSend(0x01))
   559  	go func() {
   560  		mconn.TrySend(0x01, msg)
   561  		resultCh <- "TrySend"
   562  	}()
   563  	assert.False(t, mconn.CanSend(0x01))
   564  	assert.False(t, mconn.TrySend(0x01, msg))
   565  	assert.Equal(t, "TrySend", <-resultCh)
   566  }
   567  
   568  // nolint:lll //ignore line length for tests
   569  func TestConnVectors(t *testing.T) {
   570  
   571  	testCases := []struct {
   572  		testName string
   573  		msg      proto.Message
   574  		expBytes string
   575  	}{
   576  		{"PacketPing", &tmp2p.PacketPing{}, "0a00"},
   577  		{"PacketPong", &tmp2p.PacketPong{}, "1200"},
   578  		{"PacketMsg", &tmp2p.PacketMsg{ChannelID: 1, EOF: false, Data: []byte("data transmitted over the wire")}, "1a2208011a1e64617461207472616e736d6974746564206f766572207468652077697265"},
   579  	}
   580  
   581  	for _, tc := range testCases {
   582  		tc := tc
   583  
   584  		pm := mustWrapPacket(tc.msg)
   585  		bz, err := pm.Marshal()
   586  		require.NoError(t, err, tc.testName)
   587  
   588  		require.Equal(t, tc.expBytes, hex.EncodeToString(bz), tc.testName)
   589  	}
   590  }
   591  
   592  func TestMConnectionChannelOverflow(t *testing.T) {
   593  	chOnErr := make(chan struct{})
   594  	chOnRcv := make(chan struct{})
   595  
   596  	mconnClient, mconnServer := newClientAndServerConnsForReadErrors(t, chOnErr)
   597  	t.Cleanup(stopAll(t, mconnClient, mconnServer))
   598  
   599  	mconnServer.onReceive = func(chID byte, msgBytes []byte) {
   600  		chOnRcv <- struct{}{}
   601  	}
   602  
   603  	client := mconnClient.conn
   604  	protoWriter := protoio.NewDelimitedWriter(client)
   605  
   606  	var packet = tmp2p.PacketMsg{
   607  		ChannelID: 0x01,
   608  		EOF:       true,
   609  		Data:      []byte(`42`),
   610  	}
   611  	_, err := protoWriter.WriteMsg(mustWrapPacket(&packet))
   612  	require.NoError(t, err)
   613  	assert.True(t, expectSend(chOnRcv))
   614  
   615  	packet.ChannelID = int32(1025)
   616  	_, err = protoWriter.WriteMsg(mustWrapPacket(&packet))
   617  	require.NoError(t, err)
   618  	assert.False(t, expectSend(chOnRcv))
   619  
   620  }
   621  
   622  type stopper interface {
   623  	Stop() error
   624  }
   625  
   626  func stopAll(t *testing.T, stoppers ...stopper) func() {
   627  	return func() {
   628  		for _, s := range stoppers {
   629  			if err := s.Stop(); err != nil {
   630  				t.Log(err)
   631  			}
   632  		}
   633  	}
   634  }