go.nanomsg.org/mangos/v3@v3.4.3-0.20240217232803-46464076f1f5/protocol/rep/rep_test.go (about)

     1  // Copyright 2019 The Mangos Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use file except in compliance with the License.
     5  // You may obtain a copy of the license at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package rep
    16  
    17  import (
    18  	"encoding/binary"
    19  	"sync"
    20  	"testing"
    21  	"time"
    22  
    23  	"go.nanomsg.org/mangos/v3"
    24  	. "go.nanomsg.org/mangos/v3/internal/test"
    25  	"go.nanomsg.org/mangos/v3/protocol/req"
    26  	"go.nanomsg.org/mangos/v3/protocol/xrep"
    27  	"go.nanomsg.org/mangos/v3/protocol/xreq"
    28  	_ "go.nanomsg.org/mangos/v3/transport/inproc"
    29  )
    30  
    31  func TestRepIdentity(t *testing.T) {
    32  	s := GetSocket(t, NewSocket)
    33  	id := s.Info()
    34  	MustBeTrue(t, id.Self == mangos.ProtoRep)
    35  	MustBeTrue(t, id.SelfName == "rep")
    36  	MustBeTrue(t, id.Peer == mangos.ProtoReq)
    37  	MustBeTrue(t, id.PeerName == "req")
    38  	MustSucceed(t, s.Close())
    39  }
    40  
    41  func TestRepCooked(t *testing.T) {
    42  	VerifyCooked(t, NewSocket)
    43  }
    44  
    45  func TestRepOptions(t *testing.T) {
    46  	VerifyInvalidOption(t, NewSocket)
    47  	VerifyOptionQLen(t, NewSocket, mangos.OptionWriteQLen)
    48  	VerifyOptionDuration(t, NewSocket, mangos.OptionRecvDeadline)
    49  	VerifyOptionDuration(t, NewSocket, mangos.OptionSendDeadline)
    50  	VerifyOptionBool(t, NewSocket, mangos.OptionBestEffort)
    51  	VerifyOptionInt(t, NewSocket, mangos.OptionTTL)
    52  }
    53  
    54  func TestRepClosed(t *testing.T) {
    55  	VerifyClosedRecv(t, NewSocket)
    56  	VerifyClosedSend(t, NewSocket)
    57  	VerifyClosedClose(t, NewSocket)
    58  	VerifyClosedDial(t, NewSocket)
    59  	VerifyClosedListen(t, NewSocket)
    60  	VerifyClosedAddPipe(t, NewSocket)
    61  	VerifyClosedContext(t, NewSocket)
    62  }
    63  
    64  func TestRepTTLZero(t *testing.T) {
    65  	SetTTLZero(t, NewSocket)
    66  }
    67  
    68  func TestRepTTLNegative(t *testing.T) {
    69  	SetTTLNegative(t, NewSocket)
    70  }
    71  
    72  func TestRepTTLTooBig(t *testing.T) {
    73  	SetTTLTooBig(t, NewSocket)
    74  }
    75  
    76  func TestRepTTLSet(t *testing.T) {
    77  	SetTTL(t, NewSocket)
    78  }
    79  
    80  func TestRepTTLDrop(t *testing.T) {
    81  	TTLDropTest(t, req.NewSocket, NewSocket, xreq.NewSocket, xrep.NewSocket)
    82  }
    83  
    84  func TestRepCloseRecv(t *testing.T) {
    85  	self := GetSocket(t, NewSocket)
    86  
    87  	time.AfterFunc(time.Millisecond*20, func() {
    88  		MustSucceed(t, self.Close())
    89  	})
    90  	MustNotRecv(t, self, mangos.ErrClosed)
    91  }
    92  
    93  func TestRepSendState(t *testing.T) {
    94  	s := GetSocket(t, NewSocket)
    95  	MustBeError(t, s.Send([]byte{}), mangos.ErrProtoState)
    96  	MustSucceed(t, s.Close())
    97  }
    98  
    99  func TestRepBestEffortSend(t *testing.T) {
   100  	s := GetSocket(t, NewSocket)
   101  	p := GetSocket(t, xreq.NewSocket)
   102  	MustSucceed(t, s.SetOption(mangos.OptionWriteQLen, 1))
   103  	MustSucceed(t, p.SetOption(mangos.OptionReadQLen, 1))
   104  	MustSucceed(t, s.SetOption(mangos.OptionBestEffort, true))
   105  
   106  	ConnectPair(t, s, p)
   107  	for i := 0; i < 100; i++ {
   108  		// We have to make a raw message when using xreq.  We
   109  		// use xreq because normal req will simply discard
   110  		// messages for requests it doesn't have outstanding.
   111  		m := mangos.NewMessage(0)
   112  		m.Header = make([]byte, 4)
   113  		binary.BigEndian.PutUint32(m.Header, uint32(i)|0x80000000)
   114  		MustSucceed(t, p.SendMsg(m))
   115  		m = MustRecvMsg(t, s)
   116  		MustSucceed(t, s.SendMsg(m))
   117  		// NB: We never ask the peer to receive it -- this ensures we
   118  		// encounter back-pressure.
   119  	}
   120  	MustSucceed(t, s.Close())
   121  	MustSucceed(t, p.Close())
   122  }
   123  
   124  // This verifies that closing the socket aborts a blocking send.
   125  // We use a context because closing the socket also closes pipes
   126  // making it less reproducible.
   127  func TestRepCloseContextSend(t *testing.T) {
   128  	self := GetSocket(t, NewSocket)
   129  	peer := GetSocket(t, xreq.NewSocket)
   130  	MustSucceed(t, self.SetOption(mangos.OptionWriteQLen, 1))
   131  	MustSucceed(t, peer.SetOption(mangos.OptionReadQLen, 1))
   132  	c, e := self.OpenContext()
   133  	MustSucceed(t, e)
   134  
   135  	ConnectPair(t, self, peer)
   136  
   137  	MustSucceed(t, c.SetOption(mangos.OptionSendDeadline, time.Millisecond*10))
   138  
   139  	time.Sleep(time.Millisecond * 10)
   140  	cnt := 0
   141  	data := []byte{0x80, 0, 0, 1}
   142  	for i := 0; i < 100; i++ {
   143  		// We have to make a raw message when using xreq.
   144  		m := mangos.NewMessage(0)
   145  		m.Header = append(m.Header, data...)
   146  		data[3]++
   147  		MustSucceed(t, peer.SendMsg(m))
   148  		m, e := c.RecvMsg()
   149  		MustSucceed(t, e)
   150  		MustNotBeNil(t, m)
   151  		if e = c.SendMsg(m); e != nil {
   152  			MustBeError(t, e, mangos.ErrSendTimeout)
   153  			break
   154  		}
   155  		cnt++
   156  
   157  		// NB: We never ask the peer to receive it -- this ensures we
   158  		// encounter back-pressure.
   159  	}
   160  	MustBeTrue(t, cnt > 0) // Some in-flight sends possible.
   161  	MustBeTrue(t, cnt < 10)
   162  
   163  	m := mangos.NewMessage(0)
   164  	m.Header = append(m.Header, data...)
   165  	data[3]++
   166  	MustSucceed(t, peer.SendMsg(m))
   167  
   168  	MustSucceed(t, c.SetOption(mangos.OptionSendDeadline, time.Minute))
   169  	m, e = c.RecvMsg()
   170  	MustSucceed(t, e)
   171  	MustNotBeNil(t, m)
   172  	time.AfterFunc(time.Millisecond*20, func() { MustSucceed(t, c.Close()) })
   173  	MustBeError(t, c.SendMsg(m), mangos.ErrClosed)
   174  
   175  	MustSucceed(t, peer.Close())
   176  	MustSucceed(t, self.Close())
   177  }
   178  
   179  func TestRepRecvJunk(t *testing.T) {
   180  	self := GetSocket(t, NewSocket)
   181  	mock, _ := MockConnect(t, self)
   182  
   183  	// Absent header...
   184  	MockMustSendStr(t, mock, "", time.Second)
   185  
   186  	// Absent request id... (must have bit 31 set)
   187  	MockMustSend(t, mock, []byte{1, 2, 3, 4}, time.Second)
   188  
   189  	time.Sleep(time.Millisecond * 10)
   190  	MustSucceed(t, self.Close())
   191  }
   192  
   193  func TestRepDoubleRecv(t *testing.T) {
   194  	self := GetSocket(t, NewSocket)
   195  	MustSucceed(t, self.SetOption(mangos.OptionRecvDeadline, time.Second))
   196  	pass := false
   197  	var wg sync.WaitGroup
   198  	wg.Add(1)
   199  	go func() {
   200  		defer wg.Done()
   201  		time.Sleep(time.Millisecond)
   202  		MustNotRecv(t, self, mangos.ErrProtoState)
   203  		MustSucceed(t, self.Close())
   204  		pass = true
   205  	}()
   206  	MustNotRecv(t, self, mangos.ErrClosed)
   207  	wg.Wait()
   208  	MustBeTrue(t, pass)
   209  }
   210  
   211  func TestRepClosedReply(t *testing.T) {
   212  	self := GetSocket(t, NewSocket)
   213  	peer := GetSocket(t, req.NewSocket)
   214  	ConnectPair(t, self, peer)
   215  
   216  	MustSendString(t, peer, "ping")
   217  	MustRecvString(t, self, "ping")
   218  	MustSucceed(t, peer.Close())
   219  	time.Sleep(time.Millisecond * 20)
   220  	MustSendString(t, self, "nack")
   221  	time.Sleep(time.Millisecond * 20)
   222  	MustSucceed(t, self.Close())
   223  }
   224  
   225  // This exercises the discard of inbound messages waiting, when the pipe
   226  // or socket is closed.
   227  func TestRepPipeRecvClosePipe(t *testing.T) {
   228  	self := GetSocket(t, NewSocket)
   229  	peer := GetSocket(t, req.NewSocket)
   230  	MustSucceed(t, peer.SetOption(mangos.OptionSendDeadline, time.Millisecond*10))
   231  	ConnectPair(t, self, peer)
   232  
   233  	MustSendString(t, peer, "")
   234  	m := MustRecvMsg(t, self)
   235  
   236  	for i := 0; i < 10; i++ {
   237  		e := peer.Send([]byte{})
   238  		if e != nil {
   239  			MustBeError(t, e, mangos.ErrSendTimeout)
   240  			break
   241  		}
   242  	}
   243  
   244  	MustSucceed(t, m.Pipe.Close())
   245  	time.Sleep(time.Millisecond * 10)
   246  	MustSucceed(t, peer.Close())
   247  	MustSucceed(t, self.Close())
   248  }
   249  
   250  func TestRepPipeRecvCloseSocket(t *testing.T) {
   251  	self := GetSocket(t, NewSocket)
   252  	peer := GetSocket(t, req.NewSocket)
   253  	MustSucceed(t, peer.SetOption(mangos.OptionSendDeadline, time.Millisecond*10))
   254  	ConnectPair(t, self, peer)
   255  
   256  	for i := 0; i < 10; i++ {
   257  		e := peer.Send([]byte{})
   258  		if e != nil {
   259  			MustBeError(t, e, mangos.ErrSendTimeout)
   260  			break
   261  		}
   262  	}
   263  
   264  	MustSucceed(t, self.Close())
   265  }
   266  
   267  // This sets up a bunch of contexts to run in parallel, and verifies that
   268  // they all seem to run with no mis-deliveries.
   269  func TestRespondentMultiContexts(t *testing.T) {
   270  	count := 30
   271  	repeat := 20
   272  
   273  	s := GetSocket(t, NewSocket)
   274  	p := GetSocket(t, req.NewSocket)
   275  
   276  	ConnectPair(t, p, s)
   277  
   278  	recv := make([]int, count)
   279  	send := make([]int, count)
   280  
   281  	var wg1 sync.WaitGroup
   282  	fn := func(c1, c2 mangos.Context, index int) {
   283  		defer wg1.Done()
   284  
   285  		topic := make([]byte, 4)
   286  		binary.BigEndian.PutUint32(topic, uint32(index))
   287  
   288  		for i := 0; i < repeat; i++ {
   289  			MustSucceed(t, c2.Send(topic))
   290  			m, e := c1.RecvMsg()
   291  			MustSucceed(t, e)
   292  			MustBeTrue(t, len(m.Body) == 4)
   293  			peer := binary.BigEndian.Uint32(m.Body)
   294  			recv[int(peer)]++
   295  			MustSucceed(t, c1.Send([]byte("answer")))
   296  			b, e := c2.Recv()
   297  			MustSucceed(t, e)
   298  			MustBeTrue(t, string(b) == "answer")
   299  			send[index]++
   300  		}
   301  	}
   302  
   303  	wg1.Add(count)
   304  
   305  	for i := 0; i < count; i++ {
   306  		c1, e := s.OpenContext()
   307  		MustSucceed(t, e)
   308  		MustNotBeNil(t, c1)
   309  
   310  		c2, e := p.OpenContext()
   311  		MustSucceed(t, e)
   312  		MustNotBeNil(t, c2)
   313  
   314  		MustSucceed(t, c1.SetOption(mangos.OptionRecvDeadline, time.Minute/4))
   315  		MustSucceed(t, c1.SetOption(mangos.OptionSendDeadline, time.Minute/4))
   316  		MustSucceed(t, c2.SetOption(mangos.OptionSendDeadline, time.Minute/4))
   317  		MustSucceed(t, c2.SetOption(mangos.OptionRecvDeadline, time.Minute/4))
   318  
   319  		go fn(c1, c2, i)
   320  	}
   321  
   322  	// Give time for everything to be delivered.
   323  	wg1.Wait()
   324  	MustSucceed(t, p.Close())
   325  	MustSucceed(t, s.Close())
   326  
   327  	for i := 0; i < count; i++ {
   328  		MustBeTrue(t, recv[i] == repeat)
   329  		MustBeTrue(t, send[i] == repeat)
   330  	}
   331  }