github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/network/test/cohort2/echoengine_test.go (about)

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