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