github.com/koko1123/flow-go-1@v0.29.6/network/test/echoengine_test.go (about)

     1  package test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"strings"
     8  	"sync"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/koko1123/flow-go-1/network/p2p"
    13  
    14  	"github.com/ipfs/go-log"
    15  	"github.com/rs/zerolog"
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  	"github.com/stretchr/testify/suite"
    19  
    20  	"github.com/koko1123/flow-go-1/model/flow"
    21  	"github.com/koko1123/flow-go-1/model/libp2p/message"
    22  	"github.com/koko1123/flow-go-1/module/irrecoverable"
    23  	"github.com/koko1123/flow-go-1/network"
    24  	"github.com/koko1123/flow-go-1/network/channels"
    25  	"github.com/koko1123/flow-go-1/network/internal/testutils"
    26  	"github.com/koko1123/flow-go-1/network/mocknetwork"
    27  	"github.com/koko1123/flow-go-1/utils/unittest"
    28  )
    29  
    30  // EchoEngineTestSuite tests the correctness of the entire pipeline of network -> middleware -> libp2p
    31  // protocol stack. It creates two instances of a stubengine, connects them through network, and sends a
    32  // single message from one engine to the other one through different scenarios.
    33  type EchoEngineTestSuite struct {
    34  	suite.Suite
    35  	testutils.ConduitWrapper                      // used as a wrapper around conduit methods
    36  	nets                     []network.Network    // used to keep track of the networks
    37  	mws                      []network.Middleware // used to keep track of the middlewares
    38  	ids                      flow.IdentityList    // used to keep track of the identifiers associated with networks
    39  	cancel                   context.CancelFunc
    40  }
    41  
    42  // Some tests are skipped in short mode to speedup the build.
    43  
    44  // TestStubEngineTestSuite runs all the test methods in this test suit
    45  func TestStubEngineTestSuite(t *testing.T) {
    46  	suite.Run(t, new(EchoEngineTestSuite))
    47  }
    48  
    49  func (suite *EchoEngineTestSuite) SetupTest() {
    50  	const count = 2
    51  	logger := zerolog.New(os.Stderr).Level(zerolog.ErrorLevel)
    52  	log.SetAllLoggers(log.LevelError)
    53  
    54  	ctx, cancel := context.WithCancel(context.Background())
    55  	suite.cancel = cancel
    56  
    57  	signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx)
    58  
    59  	// both nodes should be of the same role to get connected on epidemic dissemination
    60  	var nodes []p2p.LibP2PNode
    61  	suite.ids, nodes, suite.mws, suite.nets, _ = testutils.GenerateIDsMiddlewaresNetworks(
    62  		suite.T(),
    63  		count,
    64  		logger,
    65  		unittest.NetworkCodec(),
    66  		mocknetwork.NewViolationsConsumer(suite.T()),
    67  	)
    68  
    69  	testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.nets, 100*time.Millisecond)
    70  }
    71  
    72  // TearDownTest closes the networks within a specified timeout
    73  func (suite *EchoEngineTestSuite) TearDownTest() {
    74  	suite.cancel()
    75  	testutils.StopComponents(suite.T(), suite.nets, 3*time.Second)
    76  	testutils.StopComponents(suite.T(), suite.mws, 3*time.Second)
    77  }
    78  
    79  // TestUnknownChannel evaluates that registering an engine with an unknown channel returns an error.
    80  // All channels should be registered as topics in engine.topicMap.
    81  func (suite *EchoEngineTestSuite) TestUnknownChannel() {
    82  	e := NewEchoEngine(suite.T(), suite.nets[0], 1, channels.TestNetworkChannel, false, suite.Unicast)
    83  	_, err := suite.nets[0].Register("unknown-channel-id", e)
    84  	require.Error(suite.T(), err)
    85  }
    86  
    87  // TestClusterChannel evaluates that registering a cluster channel  is done without any error.
    88  func (suite *EchoEngineTestSuite) TestClusterChannel() {
    89  	e := NewEchoEngine(suite.T(), suite.nets[0], 1, channels.TestNetworkChannel, false, suite.Unicast)
    90  	// creates a cluster channel
    91  	clusterChannel := channels.SyncCluster(flow.Testnet)
    92  	// registers engine with cluster channel
    93  	_, err := suite.nets[0].Register(clusterChannel, e)
    94  	// registering cluster channel should not cause an error
    95  	require.NoError(suite.T(), err)
    96  }
    97  
    98  // TestDuplicateChannel evaluates that registering an engine with duplicate channel returns an error.
    99  func (suite *EchoEngineTestSuite) TestDuplicateChannel() {
   100  	// creates an echo engine, which registers it on test network channel
   101  	e := NewEchoEngine(suite.T(), suite.nets[0], 1, channels.TestNetworkChannel, false, suite.Unicast)
   102  
   103  	// attempts to register the same engine again on test network channel which
   104  	// should cause an error
   105  	_, err := suite.nets[0].Register(channels.TestNetworkChannel, e)
   106  	require.Error(suite.T(), err)
   107  }
   108  
   109  // TestEchoMultiMsgAsync_Publish tests sending multiple messages from sender to receiver
   110  // using the Publish method of their Conduit.
   111  // It also evaluates the correct reception of an echo message back for each send.
   112  // Sender and receiver are not synchronized
   113  func (suite *EchoEngineTestSuite) TestEchoMultiMsgAsync_Publish() {
   114  	// set to true for an echo expectation
   115  	suite.multiMessageAsync(true, 10, suite.Publish)
   116  }
   117  
   118  // TestEchoMultiMsgAsync_Unicast tests sending multiple messages from sender to receiver
   119  // using the Unicast method of their Conduit.
   120  // It also evaluates the correct reception of an echo message back for each send.
   121  // Sender and receiver are not synchronized
   122  func (suite *EchoEngineTestSuite) TestEchoMultiMsgAsync_Unicast() {
   123  	// set to true for an echo expectation
   124  	suite.multiMessageAsync(true, 10, suite.Unicast)
   125  }
   126  
   127  // TestEchoMultiMsgAsync_Multicast tests sending multiple messages from sender to receiver
   128  // using the Multicast method of their Conduit.
   129  // It also evaluates the correct reception of an echo message back for each send.
   130  // Sender and receiver are not synchronized
   131  func (suite *EchoEngineTestSuite) TestEchoMultiMsgAsync_Multicast() {
   132  	// set to true for an echo expectation
   133  	suite.multiMessageAsync(true, 10, suite.Multicast)
   134  }
   135  
   136  // TestDuplicateMessageSequential_Publish evaluates the correctness of network layer on deduplicating
   137  // the received messages over Publish method of nodes' Conduits.
   138  // Messages are delivered to the receiver in a sequential manner.
   139  func (suite *EchoEngineTestSuite) TestDuplicateMessageSequential_Publish() {
   140  	unittest.SkipUnless(suite.T(), unittest.TEST_LONG_RUNNING, "covered by TestDuplicateMessageParallel_Publish")
   141  	suite.duplicateMessageSequential(suite.Publish)
   142  }
   143  
   144  // TestDuplicateMessageSequential_Unicast evaluates the correctness of network layer on deduplicating
   145  // the received messages over Unicast method of nodes' Conduits.
   146  // Messages are delivered to the receiver in a sequential manner.
   147  func (suite *EchoEngineTestSuite) TestDuplicateMessageSequential_Unicast() {
   148  	unittest.SkipUnless(suite.T(), unittest.TEST_LONG_RUNNING, "covered by TestDuplicateMessageParallel_Unicast")
   149  	suite.duplicateMessageSequential(suite.Unicast)
   150  }
   151  
   152  // TestDuplicateMessageSequential_Multicast evaluates the correctness of network layer on deduplicating
   153  // the received messages over Multicast method of nodes' Conduits.
   154  // Messages are delivered to the receiver in a sequential manner.
   155  func (suite *EchoEngineTestSuite) TestDuplicateMessageSequential_Multicast() {
   156  	unittest.SkipUnless(suite.T(), unittest.TEST_LONG_RUNNING, "covered by TestDuplicateMessageParallel_Multicast")
   157  	suite.duplicateMessageSequential(suite.Multicast)
   158  }
   159  
   160  // TestDuplicateMessageParallel_Publish evaluates the correctness of network layer
   161  // on deduplicating the received messages via Publish method of nodes' Conduits.
   162  // Messages are delivered to the receiver in parallel via the Publish method of Conduits.
   163  func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Publish() {
   164  	suite.duplicateMessageParallel(suite.Publish)
   165  }
   166  
   167  // TestDuplicateMessageParallel_Unicast evaluates the correctness of network layer
   168  // on deduplicating the received messages via Unicast method of nodes' Conduits.
   169  // Messages are delivered to the receiver in parallel via the Unicast method of Conduits.
   170  func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Unicast() {
   171  	suite.duplicateMessageParallel(suite.Unicast)
   172  }
   173  
   174  // TestDuplicateMessageParallel_Multicast evaluates the correctness of network layer
   175  // on deduplicating the received messages via Multicast method of nodes' Conduits.
   176  // Messages are delivered to the receiver in parallel via the Multicast method of Conduits.
   177  func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Multicast() {
   178  	suite.duplicateMessageParallel(suite.Multicast)
   179  }
   180  
   181  // TestDuplicateMessageDifferentChan_Publish evaluates the correctness of network layer
   182  // on deduplicating the received messages against different engine ids. In specific, the
   183  // desire behavior is that the deduplication should happen based on both eventID and channel.
   184  // Messages are sent via the Publish methods of the Conduits.
   185  func (suite *EchoEngineTestSuite) TestDuplicateMessageDifferentChan_Publish() {
   186  	suite.duplicateMessageDifferentChan(suite.Publish)
   187  }
   188  
   189  // TestDuplicateMessageDifferentChan_Unicast evaluates the correctness of network layer
   190  // on deduplicating the received messages against different engine ids. In specific, the
   191  // desire behavior is that the deduplication should happen based on both eventID and channel.
   192  // Messages are sent via the Unicast methods of the Conduits.
   193  func (suite *EchoEngineTestSuite) TestDuplicateMessageDifferentChan_Unicast() {
   194  	suite.duplicateMessageDifferentChan(suite.Unicast)
   195  }
   196  
   197  // TestDuplicateMessageDifferentChan_Multicast evaluates the correctness of network layer
   198  // on deduplicating the received messages against different engine ids. In specific, the
   199  // desire behavior is that the deduplication should happen based on both eventID and channel.
   200  // Messages are sent via the Multicast methods of the Conduits.
   201  func (suite *EchoEngineTestSuite) TestDuplicateMessageDifferentChan_Multicast() {
   202  	suite.duplicateMessageDifferentChan(suite.Multicast)
   203  }
   204  
   205  // duplicateMessageSequential is a helper function that sends duplicate messages sequentially
   206  // from a receiver to the sender via the injected send wrapper function of conduit.
   207  func (suite *EchoEngineTestSuite) duplicateMessageSequential(send testutils.ConduitSendWrapperFunc) {
   208  	sndID := 0
   209  	rcvID := 1
   210  	// registers engines in the network
   211  	// sender's engine
   212  	sender := NewEchoEngine(suite.Suite.T(), suite.nets[sndID], 10, channels.TestNetworkChannel, false, send)
   213  
   214  	// receiver's engine
   215  	receiver := NewEchoEngine(suite.Suite.T(), suite.nets[rcvID], 10, channels.TestNetworkChannel, false, send)
   216  
   217  	// allow nodes to heartbeat and discover each other if using PubSub
   218  	testutils.OptionalSleep(send)
   219  
   220  	// Sends a message from sender to receiver
   221  	event := &message.TestMessage{
   222  		Text: "hello",
   223  	}
   224  
   225  	// sends the same message 10 times
   226  	for i := 0; i < 10; i++ {
   227  		require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[rcvID].NodeID))
   228  	}
   229  
   230  	time.Sleep(1 * time.Second)
   231  
   232  	// receiver should only see the message once, and the rest should be dropped due to
   233  	// duplication
   234  	receiver.RLock()
   235  	require.Equal(suite.Suite.T(), 1, receiver.seen[event.Text])
   236  	require.Len(suite.Suite.T(), receiver.seen, 1)
   237  	receiver.RUnlock()
   238  }
   239  
   240  // duplicateMessageParallel is a helper function that sends duplicate messages concurrent;u
   241  // from a receiver to the sender via the injected send wrapper function of conduit.
   242  func (suite *EchoEngineTestSuite) duplicateMessageParallel(send testutils.ConduitSendWrapperFunc) {
   243  	sndID := 0
   244  	rcvID := 1
   245  	// registers engines in the network
   246  	// sender's engine
   247  	sender := NewEchoEngine(suite.Suite.T(), suite.nets[sndID], 10, channels.TestNetworkChannel, false, send)
   248  
   249  	// receiver's engine
   250  	receiver := NewEchoEngine(suite.Suite.T(), suite.nets[rcvID], 10, channels.TestNetworkChannel, false, send)
   251  
   252  	// allow nodes to heartbeat and discover each other
   253  	testutils.OptionalSleep(send)
   254  
   255  	// Sends a message from sender to receiver
   256  	event := &message.TestMessage{
   257  		Text: "hello",
   258  	}
   259  
   260  	// sends the same message 10 times
   261  	wg := sync.WaitGroup{}
   262  	for i := 0; i < 10; i++ {
   263  		wg.Add(1)
   264  		go func() {
   265  			require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[rcvID].NodeID))
   266  			wg.Done()
   267  		}()
   268  	}
   269  	unittest.RequireReturnsBefore(suite.T(), wg.Wait, 1*time.Second, "could not send message on time")
   270  	time.Sleep(1 * time.Second)
   271  
   272  	// receiver should only see the message once, and the rest should be dropped due to
   273  	// duplication
   274  	receiver.RLock()
   275  	require.Equal(suite.Suite.T(), 1, receiver.seen[event.Text])
   276  	require.Len(suite.Suite.T(), receiver.seen, 1)
   277  	receiver.RUnlock()
   278  }
   279  
   280  // duplicateMessageDifferentChan is a helper function that sends the same message from two distinct
   281  // sender engines to the two distinct receiver engines via the send wrapper function of Conduits.
   282  func (suite *EchoEngineTestSuite) duplicateMessageDifferentChan(send testutils.ConduitSendWrapperFunc) {
   283  	const (
   284  		sndNode = iota
   285  		rcvNode
   286  	)
   287  	const (
   288  		channel1 = channels.TestNetworkChannel
   289  		channel2 = channels.TestMetricsChannel
   290  	)
   291  	// registers engines in the network
   292  	// first type
   293  	// sender'suite engine
   294  	sender1 := NewEchoEngine(suite.Suite.T(), suite.nets[sndNode], 10, channel1, false, send)
   295  
   296  	// receiver's engine
   297  	receiver1 := NewEchoEngine(suite.Suite.T(), suite.nets[rcvNode], 10, channel1, false, send)
   298  
   299  	// second type
   300  	// registers engines in the network
   301  	// sender'suite engine
   302  	sender2 := NewEchoEngine(suite.Suite.T(), suite.nets[sndNode], 10, channel2, false, send)
   303  
   304  	// receiver's engine
   305  	receiver2 := NewEchoEngine(suite.Suite.T(), suite.nets[rcvNode], 10, channel2, false, send)
   306  
   307  	// allow nodes to heartbeat and discover each other
   308  	testutils.OptionalSleep(send)
   309  
   310  	// Sends a message from sender to receiver
   311  	event := &message.TestMessage{
   312  		Text: "hello",
   313  	}
   314  
   315  	// sends the same message 10 times on both channels
   316  	wg := sync.WaitGroup{}
   317  	for i := 0; i < 10; i++ {
   318  		wg.Add(1)
   319  		go func() {
   320  			defer wg.Done()
   321  			// sender1 to receiver1 on channel1
   322  			require.NoError(suite.Suite.T(), send(event, sender1.con, suite.ids[rcvNode].NodeID))
   323  
   324  			// sender2 to receiver2 on channel2
   325  			require.NoError(suite.Suite.T(), send(event, sender2.con, suite.ids[rcvNode].NodeID))
   326  		}()
   327  	}
   328  	unittest.RequireReturnsBefore(suite.T(), wg.Wait, 1*time.Second, "could not handle sending unicasts on time")
   329  	time.Sleep(1 * time.Second)
   330  
   331  	// each receiver should only see the message once, and the rest should be dropped due to
   332  	// duplication
   333  	receiver1.RLock()
   334  	receiver2.RLock()
   335  	require.Equal(suite.Suite.T(), 1, receiver1.seen[event.Text])
   336  	require.Equal(suite.Suite.T(), 1, receiver2.seen[event.Text])
   337  
   338  	require.Len(suite.Suite.T(), receiver1.seen, 1)
   339  	require.Len(suite.Suite.T(), receiver2.seen, 1)
   340  	receiver1.RUnlock()
   341  	receiver2.RUnlock()
   342  }
   343  
   344  // singleMessage sends a single message from one network instance to the other one
   345  // it evaluates the correctness of implementation against correct delivery of the message.
   346  // in case echo is true, it also evaluates correct reception of the echo message from the receiver side
   347  func (suite *EchoEngineTestSuite) singleMessage(echo bool, send testutils.ConduitSendWrapperFunc) {
   348  	sndID := 0
   349  	rcvID := 1
   350  
   351  	// registers engines in the network
   352  	// sender's engine
   353  	sender := NewEchoEngine(suite.Suite.T(), suite.nets[sndID], 10, channels.TestNetworkChannel, echo, send)
   354  
   355  	// receiver's engine
   356  	receiver := NewEchoEngine(suite.Suite.T(), suite.nets[rcvID], 10, channels.TestNetworkChannel, echo, send)
   357  
   358  	// allow nodes to heartbeat and discover each other
   359  	testutils.OptionalSleep(send)
   360  
   361  	// Sends a message from sender to receiver
   362  	event := &message.TestMessage{
   363  		Text: "hello",
   364  	}
   365  	require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[rcvID].NodeID))
   366  
   367  	// evaluates reception of echo request
   368  	select {
   369  	case <-receiver.received:
   370  		// evaluates reception of message at the other side
   371  		// does not evaluate the content
   372  		receiver.RLock()
   373  		require.NotNil(suite.Suite.T(), receiver.originID)
   374  		require.NotNil(suite.Suite.T(), receiver.event)
   375  		assert.Equal(suite.Suite.T(), suite.ids[sndID].NodeID, receiver.originID)
   376  		receiver.RUnlock()
   377  
   378  		assertMessageReceived(suite.T(), receiver, event, channels.TestNetworkChannel)
   379  
   380  	case <-time.After(10 * time.Second):
   381  		assert.Fail(suite.Suite.T(), "sender failed to send a message to receiver")
   382  	}
   383  
   384  	// evaluates echo back
   385  	if echo {
   386  		// evaluates reception of echo response
   387  		select {
   388  		case <-sender.received:
   389  			// evaluates reception of message at the other side
   390  			// does not evaluate the content
   391  			sender.RLock()
   392  			require.NotNil(suite.Suite.T(), sender.originID)
   393  			require.NotNil(suite.Suite.T(), sender.event)
   394  			assert.Equal(suite.Suite.T(), suite.ids[rcvID].NodeID, sender.originID)
   395  			sender.RUnlock()
   396  
   397  			echoEvent := &message.TestMessage{
   398  				Text: fmt.Sprintf("%s: %s", receiver.echomsg, event.Text),
   399  			}
   400  			assertMessageReceived(suite.T(), sender, echoEvent, channels.TestNetworkChannel)
   401  
   402  		case <-time.After(10 * time.Second):
   403  			assert.Fail(suite.Suite.T(), "receiver failed to send an echo message back to sender")
   404  		}
   405  	}
   406  }
   407  
   408  // multiMessageSync sends a multiple messages from one network instance to the other one
   409  // it evaluates the correctness of implementation against correct delivery of the messages.
   410  // sender and receiver are sync over reception, i.e., sender sends one message at a time and
   411  // waits for its reception
   412  // count defines number of messages
   413  func (suite *EchoEngineTestSuite) multiMessageSync(echo bool, count int, send testutils.ConduitSendWrapperFunc) {
   414  	sndID := 0
   415  	rcvID := 1
   416  	// registers engines in the network
   417  	// sender's engine
   418  	sender := NewEchoEngine(suite.Suite.T(), suite.nets[sndID], 10, channels.TestNetworkChannel, echo, send)
   419  
   420  	// receiver's engine
   421  	receiver := NewEchoEngine(suite.Suite.T(), suite.nets[rcvID], 10, channels.TestNetworkChannel, echo, send)
   422  
   423  	// allow nodes to heartbeat and discover each other
   424  	testutils.OptionalSleep(send)
   425  
   426  	for i := 0; i < count; i++ {
   427  		// Send the message to receiver
   428  		event := &message.TestMessage{
   429  			Text: fmt.Sprintf("hello%d", i),
   430  		}
   431  		// sends a message from sender to receiver using send wrapper function
   432  		require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[rcvID].NodeID))
   433  
   434  		select {
   435  		case <-receiver.received:
   436  			// evaluates reception of message at the other side
   437  			// does not evaluate the content
   438  			receiver.RLock()
   439  			require.NotNil(suite.Suite.T(), receiver.originID)
   440  			require.NotNil(suite.Suite.T(), receiver.event)
   441  			assert.Equal(suite.Suite.T(), suite.ids[sndID].NodeID, receiver.originID)
   442  			receiver.RUnlock()
   443  
   444  			assertMessageReceived(suite.T(), receiver, event, channels.TestNetworkChannel)
   445  
   446  		case <-time.After(2 * time.Second):
   447  			assert.Fail(suite.Suite.T(), "sender failed to send a message to receiver")
   448  		}
   449  
   450  		// evaluates echo back
   451  		if echo {
   452  			// evaluates reception of echo response
   453  			select {
   454  			case <-sender.received:
   455  				// evaluates reception of message at the other side
   456  				// does not evaluate the content
   457  				sender.RLock()
   458  				require.NotNil(suite.Suite.T(), sender.originID)
   459  				require.NotNil(suite.Suite.T(), sender.event)
   460  				assert.Equal(suite.Suite.T(), suite.ids[rcvID].NodeID, sender.originID)
   461  
   462  				receiver.RLock()
   463  				echoEvent := &message.TestMessage{
   464  					Text: fmt.Sprintf("%s: %s", receiver.echomsg, event.Text),
   465  				}
   466  				assertMessageReceived(suite.T(), sender, echoEvent, channels.TestNetworkChannel)
   467  				receiver.RUnlock()
   468  				sender.RUnlock()
   469  
   470  			case <-time.After(10 * time.Second):
   471  				assert.Fail(suite.Suite.T(), "receiver failed to send an echo message back to sender")
   472  			}
   473  		}
   474  
   475  	}
   476  
   477  }
   478  
   479  // multiMessageAsync sends a multiple messages from one network instance to the other one
   480  // it evaluates the correctness of implementation against correct delivery of the messages.
   481  // sender and receiver are async, i.e., sender sends all its message at blast
   482  // count defines number of messages
   483  func (suite *EchoEngineTestSuite) multiMessageAsync(echo bool, count int, send testutils.ConduitSendWrapperFunc) {
   484  	sndID := 0
   485  	rcvID := 1
   486  
   487  	// registers engines in the network
   488  	// sender's engine
   489  	sender := NewEchoEngine(suite.Suite.T(), suite.nets[sndID], 10, channels.TestNetworkChannel, echo, send)
   490  
   491  	// receiver's engine
   492  	receiver := NewEchoEngine(suite.Suite.T(), suite.nets[rcvID], 10, channels.TestNetworkChannel, echo, send)
   493  
   494  	// allow nodes to heartbeat and discover each other
   495  	testutils.OptionalSleep(send)
   496  
   497  	// keeps track of async received messages at receiver side
   498  	received := make(map[string]struct{})
   499  
   500  	// keeps track of async received echo messages at sender side
   501  	// echorcv := make(map[string]struct{})
   502  
   503  	for i := 0; i < count; i++ {
   504  		// Send the message to node 2 using the conduit of node 1
   505  		event := &message.TestMessage{
   506  			Text: fmt.Sprintf("hello%d", i),
   507  		}
   508  		require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[1].NodeID))
   509  	}
   510  
   511  	for i := 0; i < count; i++ {
   512  		select {
   513  		case <-receiver.received:
   514  			// evaluates reception of message at the other side
   515  			// does not evaluate the content
   516  			receiver.RLock()
   517  			require.NotNil(suite.Suite.T(), receiver.originID)
   518  			require.NotNil(suite.Suite.T(), receiver.event)
   519  			assert.Equal(suite.Suite.T(), suite.ids[0].NodeID, receiver.originID)
   520  			receiver.RUnlock()
   521  
   522  			// wrap blocking channel reads with a timeout
   523  			unittest.AssertReturnsBefore(suite.T(), func() {
   524  				// evaluates proper reception of event
   525  				// casts the received event at the receiver side
   526  				rcvEvent, ok := (<-receiver.event).(*message.TestMessage)
   527  				// evaluates correctness of casting
   528  				require.True(suite.T(), ok)
   529  
   530  				// evaluates content of received message
   531  				// the content should not yet received and be unique
   532  				_, rcv := received[rcvEvent.Text]
   533  				assert.False(suite.T(), rcv)
   534  				// marking event as received
   535  				received[rcvEvent.Text] = struct{}{}
   536  
   537  				// evaluates channel that message was received on
   538  				assert.Equal(suite.T(), channels.TestNetworkChannel, <-receiver.channel)
   539  			}, 100*time.Millisecond)
   540  
   541  		case <-time.After(2 * time.Second):
   542  			assert.Fail(suite.Suite.T(), "sender failed to send a message to receiver")
   543  		}
   544  	}
   545  
   546  	for i := 0; i < count; i++ {
   547  		// evaluates echo back
   548  		if echo {
   549  			// evaluates reception of echo response
   550  			select {
   551  			case <-sender.received:
   552  				// evaluates reception of message at the other side
   553  				// does not evaluate the content
   554  				sender.RLock()
   555  				require.NotNil(suite.Suite.T(), sender.originID)
   556  				require.NotNil(suite.Suite.T(), sender.event)
   557  				assert.Equal(suite.Suite.T(), suite.ids[rcvID].NodeID, sender.originID)
   558  				sender.RUnlock()
   559  
   560  				// wrap blocking channel reads with a timeout
   561  				unittest.AssertReturnsBefore(suite.T(), func() {
   562  					// evaluates proper reception of event
   563  					// casts the received event at the receiver side
   564  					rcvEvent, ok := (<-sender.event).(*message.TestMessage)
   565  					// evaluates correctness of casting
   566  					require.True(suite.T(), ok)
   567  					// evaluates content of received echo message
   568  					// the content should not yet received and be unique
   569  					_, rcv := received[rcvEvent.Text]
   570  					assert.False(suite.T(), rcv)
   571  					// echo messages should start with prefix msg of receiver that echos back
   572  					assert.True(suite.T(), strings.HasPrefix(rcvEvent.Text, receiver.echomsg))
   573  					// marking echo event as received
   574  					received[rcvEvent.Text] = struct{}{}
   575  
   576  					// evaluates channel that message was received on
   577  					assert.Equal(suite.T(), channels.TestNetworkChannel, <-sender.channel)
   578  				}, 100*time.Millisecond)
   579  
   580  			case <-time.After(10 * time.Second):
   581  				assert.Fail(suite.Suite.T(), "receiver failed to send an echo message back to sender")
   582  			}
   583  		}
   584  	}
   585  }
   586  
   587  // assertMessageReceived asserts that the given message was received on the given channel
   588  // for the given engine
   589  func assertMessageReceived(t *testing.T, e *EchoEngine, m *message.TestMessage, c channels.Channel) {
   590  	// wrap blocking channel reads with a timeout
   591  	unittest.AssertReturnsBefore(t, func() {
   592  		// evaluates proper reception of event
   593  		// casts the received event at the receiver side
   594  		rcvEvent, ok := (<-e.event).(*message.TestMessage)
   595  		// evaluates correctness of casting
   596  		require.True(t, ok)
   597  		// evaluates content of received message
   598  		assert.Equal(t, m, rcvEvent)
   599  
   600  		// evaluates channel that message was received on
   601  		assert.Equal(t, c, <-e.channel)
   602  	}, 100*time.Millisecond)
   603  }