go.nanomsg.org/mangos/v3@v3.4.3-0.20240217232803-46464076f1f5/protocol/respondent/respondent_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 respondent
    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/surveyor"
    26  	"go.nanomsg.org/mangos/v3/protocol/xrespondent"
    27  	"go.nanomsg.org/mangos/v3/protocol/xsurveyor"
    28  	_ "go.nanomsg.org/mangos/v3/transport/inproc"
    29  )
    30  
    31  func TestRespondentIdentity(t *testing.T) {
    32  	s, err := NewSocket()
    33  	MustSucceed(t, err)
    34  	id := s.Info()
    35  	MustBeTrue(t, id.Self == mangos.ProtoRespondent)
    36  	MustBeTrue(t, id.SelfName == "respondent")
    37  	MustBeTrue(t, id.Peer == mangos.ProtoSurveyor)
    38  	MustBeTrue(t, id.PeerName == "surveyor")
    39  	MustSucceed(t, s.Close())
    40  }
    41  
    42  func TestRespondentCooked(t *testing.T) {
    43  	VerifyCooked(t, NewSocket)
    44  }
    45  
    46  func TestRespondentOptions(t *testing.T) {
    47  	VerifyInvalidOption(t, NewSocket)
    48  	VerifyOptionDuration(t, NewSocket, mangos.OptionRecvDeadline)
    49  	VerifyOptionDuration(t, NewSocket, mangos.OptionSendDeadline)
    50  	VerifyOptionInt(t, NewSocket, mangos.OptionTTL)
    51  	VerifyOptionQLen(t, NewSocket, mangos.OptionReadQLen)
    52  	VerifyOptionQLen(t, NewSocket, mangos.OptionWriteQLen)
    53  	VerifyOptionBool(t, NewSocket, mangos.OptionBestEffort)
    54  }
    55  
    56  func TestRespondentClosed(t *testing.T) {
    57  	VerifyClosedRecv(t, NewSocket)
    58  	VerifyClosedSend(t, NewSocket)
    59  	VerifyClosedClose(t, NewSocket)
    60  	VerifyClosedDial(t, NewSocket)
    61  	VerifyClosedListen(t, NewSocket)
    62  	VerifyClosedAddPipe(t, NewSocket)
    63  }
    64  
    65  func TestRespondentTTLZero(t *testing.T) {
    66  	SetTTLZero(t, NewSocket)
    67  }
    68  
    69  func TestRespondentTTLNegative(t *testing.T) {
    70  	SetTTLNegative(t, NewSocket)
    71  }
    72  
    73  func TestRespondentTTLTooBig(t *testing.T) {
    74  	SetTTLTooBig(t, NewSocket)
    75  }
    76  
    77  func TestRespondentTTLNotInt(t *testing.T) {
    78  	SetTTLNotInt(t, NewSocket)
    79  }
    80  
    81  func TestRespondentTTLSet(t *testing.T) {
    82  	SetTTL(t, NewSocket)
    83  }
    84  
    85  func TestRespondentTTLDrop(t *testing.T) {
    86  	TTLDropTest(t, surveyor.NewSocket, NewSocket, xsurveyor.NewSocket, xrespondent.NewSocket)
    87  }
    88  
    89  // This test demonstrates that closing the socket aborts outstanding receives.
    90  func TestRespondentCloseRx(t *testing.T) {
    91  	s := GetSocket(t, NewSocket)
    92  	var wg sync.WaitGroup
    93  	wg.Add(1)
    94  	pass := false
    95  	go func() {
    96  		defer wg.Done()
    97  		v, e := s.Recv()
    98  		MustBeError(t, e, mangos.ErrClosed)
    99  		MustBeNil(t, v)
   100  		pass = true
   101  	}()
   102  	time.Sleep(time.Millisecond * 10) // to allow go routine to run
   103  	MustSucceed(t, s.Close())
   104  	wg.Wait()
   105  	MustBeTrue(t, pass)
   106  }
   107  
   108  func TestRespondentCloseSocketRecv(t *testing.T) {
   109  	s := GetSocket(t, NewSocket)
   110  	p := GetSocket(t, surveyor.NewSocket)
   111  	ConnectPair(t, s, p)
   112  	MustSucceed(t, s.SetOption(mangos.OptionReadQLen, 1))
   113  
   114  	// Fill the pipe
   115  	for i := 0; i < 10; i++ {
   116  		// These all will work, but the back-pressure will go all the
   117  		// way to the sender.
   118  		MustSucceed(t, p.Send([]byte("")))
   119  	}
   120  	MustSucceed(t, s.Close())
   121  }
   122  
   123  func TestRepRespondentJunk(t *testing.T) {
   124  	self := GetSocket(t, NewSocket)
   125  	mock, _ := MockConnect(t, self)
   126  
   127  	// Absent header...
   128  	MockMustSendStr(t, mock, "", time.Second)
   129  
   130  	// Absent request id... (must have bit 31 set)
   131  	MockMustSend(t, mock, []byte{1, 2, 3, 4}, time.Second)
   132  
   133  	time.Sleep(time.Millisecond * 10)
   134  	MustSucceed(t, self.Close())
   135  }
   136  
   137  // This test fills our receive queue, then truncates it.
   138  func TestRespondentResizeRecv(t *testing.T) {
   139  	s := GetSocket(t, NewSocket)
   140  	p := GetSocket(t, surveyor.NewSocket)
   141  	MustSucceed(t, p.SetOption(mangos.OptionWriteQLen, 0))
   142  	MustSucceed(t, s.SetOption(mangos.OptionReadQLen, 20))
   143  	MustSucceed(t, s.SetOption(mangos.OptionRecvDeadline, time.Millisecond))
   144  	ConnectPair(t, s, p)
   145  
   146  	// Fill the pipe
   147  	for i := 0; i < 20; i++ {
   148  		// These all will work, but the back-pressure will go all the
   149  		// way to the sender.
   150  		MustSucceed(t, p.Send([]byte{byte(i)}))
   151  	}
   152  
   153  	time.Sleep(time.Millisecond * 10)
   154  	MustSucceed(t, s.SetOption(mangos.OptionReadQLen, 1))
   155  	// Sleep so the resize filler finishes
   156  	time.Sleep(time.Millisecond * 20)
   157  
   158  	cnt := 0
   159  	for i := 0; i < 20; i++ {
   160  		if _, e := s.Recv(); e != nil {
   161  			MustBeError(t, e, mangos.ErrRecvTimeout)
   162  			break
   163  		}
   164  		cnt++
   165  	}
   166  	MustSucceed(t, s.Close())
   167  }
   168  
   169  // This tests that a waiting pipe reader will shift to the new pipe.
   170  func TestRespondentResizeRecv2(t *testing.T) {
   171  	s := GetSocket(t, NewSocket)
   172  	p := GetSocket(t, surveyor.NewSocket)
   173  	MustSucceed(t, p.SetOption(mangos.OptionWriteQLen, 0))
   174  	MustSucceed(t, s.SetOption(mangos.OptionReadQLen, 2))
   175  	MustSucceed(t, s.SetOption(mangos.OptionRecvDeadline, time.Millisecond))
   176  	ConnectPair(t, s, p)
   177  
   178  	// Fill the pipe
   179  	for i := 0; i < 20; i++ {
   180  		// These all will work, but the back-pressure will go all the
   181  		// way to the sender.
   182  		MustSucceed(t, p.Send([]byte{byte(i)}))
   183  		time.Sleep(time.Millisecond * 5)
   184  	}
   185  
   186  	time.Sleep(time.Millisecond * 20)
   187  	MustSucceed(t, s.SetOption(mangos.OptionReadQLen, 1))
   188  	cnt := 0
   189  	for i := 0; i < 20; i++ {
   190  		if _, e := s.Recv(); e != nil {
   191  			MustBeError(t, e, mangos.ErrRecvTimeout)
   192  			break
   193  		}
   194  		cnt++
   195  	}
   196  	MustSucceed(t, s.Close())
   197  }
   198  
   199  // This tests that a posted recv sees the resize and moves to the new queue.
   200  func TestRespondentResizeRecv3(t *testing.T) {
   201  	s := GetSocket(t, NewSocket)
   202  	p := GetSocket(t, surveyor.NewSocket)
   203  	MustSucceed(t, p.SetOption(mangos.OptionWriteQLen, 0))
   204  	MustSucceed(t, s.SetOption(mangos.OptionReadQLen, 10))
   205  	MustSucceed(t, s.SetOption(mangos.OptionRecvDeadline, time.Minute))
   206  	ConnectPair(t, s, p)
   207  
   208  	MustSucceed(t, s.SetOption(mangos.OptionReadQLen, 1))
   209  	time.AfterFunc(time.Millisecond*10, func() {
   210  		MustSucceed(t, s.SetOption(mangos.OptionReadQLen, 2))
   211  		time.Sleep(10 * time.Millisecond)
   212  		MustSucceed(t, p.Send([]byte{}))
   213  	})
   214  	m, e := s.Recv()
   215  	MustSucceed(t, e)
   216  	MustNotBeNil(t, m)
   217  	MustSucceed(t, s.Close())
   218  	MustSucceed(t, p.Close())
   219  }
   220  
   221  func TestRespondentClosePipeRecv(t *testing.T) {
   222  	s := GetSocket(t, NewSocket)
   223  	p := GetSocket(t, surveyor.NewSocket)
   224  	ConnectPair(t, s, p)
   225  	MustSucceed(t, s.SetOption(mangos.OptionReadQLen, 1))
   226  	MustSucceed(t, p.Send([]byte("")))
   227  	m, e := s.RecvMsg()
   228  	MustSucceed(t, e)
   229  
   230  	// Fill the pipe
   231  	for i := 0; i < 10; i++ {
   232  		// These all will work, but the back-pressure will go all the
   233  		// way to the sender.
   234  		MustSucceed(t, p.Send([]byte("")))
   235  	}
   236  	MustSucceed(t, m.Pipe.Close())
   237  	time.Sleep(time.Millisecond * 10)
   238  	MustSucceed(t, s.Close())
   239  	MustSucceed(t, p.Close())
   240  }
   241  
   242  func TestRespondentClosePipeSend(t *testing.T) {
   243  	s := GetSocket(t, NewSocket)
   244  	p := GetSocket(t, surveyor.NewSocket)
   245  	ConnectPair(t, s, p)
   246  	MustSucceed(t, s.SetOption(mangos.OptionReadQLen, 1))
   247  	MustSucceed(t, p.Send([]byte("")))
   248  	m, e := s.RecvMsg()
   249  	MustSucceed(t, e)
   250  	MustSucceed(t, m.Pipe.Close())
   251  	time.Sleep(time.Millisecond * 10)
   252  	MustSucceed(t, s.SendMsg(m))
   253  	time.Sleep(time.Millisecond * 10)
   254  	MustSucceed(t, s.Close())
   255  	MustSucceed(t, p.Close())
   256  }
   257  
   258  func TestRespondentRecvExpire(t *testing.T) {
   259  	s := GetSocket(t, NewSocket)
   260  	MustSucceed(t, s.SetOption(mangos.OptionRecvDeadline, time.Millisecond))
   261  	v, e := s.RecvMsg()
   262  	MustBeError(t, e, mangos.ErrRecvTimeout)
   263  	MustBeNil(t, v)
   264  	MustSucceed(t, s.Close())
   265  }
   266  
   267  func TestRespondentSendState(t *testing.T) {
   268  	s := GetSocket(t, NewSocket)
   269  	MustBeError(t, s.Send([]byte{}), mangos.ErrProtoState)
   270  	MustSucceed(t, s.Close())
   271  }
   272  
   273  func TestRespondentBestEffortSend(t *testing.T) {
   274  	s := GetSocket(t, NewSocket)
   275  	p := GetSocket(t, xsurveyor.NewSocket)
   276  	MustSucceed(t, s.SetOption(mangos.OptionWriteQLen, 1))
   277  	MustSucceed(t, p.SetOption(mangos.OptionReadQLen, 1))
   278  	MustSucceed(t, s.SetOption(mangos.OptionBestEffort, true))
   279  
   280  	ConnectPair(t, s, p)
   281  	for i := 0; i < 100; i++ {
   282  		// We have to make a raw message when using xsurveyor.  We
   283  		// use xsurveyor because normal surveyor will simply discard
   284  		// messages for surveys it doesn't have outstanding.
   285  		m := mangos.NewMessage(0)
   286  		m.Header = make([]byte, 4)
   287  		binary.BigEndian.PutUint32(m.Header, uint32(i)|0x80000000)
   288  		MustSucceed(t, p.SendMsg(m))
   289  		m, e := s.RecvMsg()
   290  		MustSucceed(t, e)
   291  		MustNotBeNil(t, m)
   292  		MustSucceed(t, s.SendMsg(m))
   293  		// NB: We never ask the peer to receive it -- this ensures we
   294  		// encounter backpressure.
   295  	}
   296  	MustSucceed(t, s.Close())
   297  	MustSucceed(t, p.Close())
   298  }
   299  
   300  func TestRespondentSendBackPressure(t *testing.T) {
   301  	s := GetSocket(t, NewSocket)
   302  	p := GetSocket(t, xsurveyor.NewSocket)
   303  	MustSucceed(t, s.SetOption(mangos.OptionWriteQLen, 1))
   304  	MustSucceed(t, p.SetOption(mangos.OptionReadQLen, 1))
   305  
   306  	ConnectPair(t, s, p)
   307  
   308  	MustSucceed(t, s.SetOption(mangos.OptionSendDeadline, time.Millisecond*10))
   309  
   310  	cnt := 0
   311  	for i := 0; i < 100; i++ {
   312  		// We have to make a raw message when using xsurveyor.  We
   313  		// use xsurveyor because normal surveyor will simply discard
   314  		// messages for surveys it doesn't have outstanding.
   315  		m := mangos.NewMessage(0)
   316  		m.Header = make([]byte, 4)
   317  		binary.BigEndian.PutUint32(m.Header, uint32(i)|0x80000000)
   318  		MustSucceed(t, p.SendMsg(m))
   319  		m, e := s.RecvMsg()
   320  		MustSucceed(t, e)
   321  		MustNotBeNil(t, m)
   322  		if e = s.SendMsg(m); e != nil {
   323  			MustBeError(t, e, mangos.ErrSendTimeout)
   324  			break
   325  		}
   326  		cnt++
   327  		// NB: We never ask the peer to receive it -- this ensures we
   328  		// encounter back-pressure.
   329  	}
   330  	MustBeTrue(t, cnt > 0) // Some in-flight sends possible.
   331  	MustBeTrue(t, cnt < 10)
   332  	MustSucceed(t, s.Close())
   333  	MustSucceed(t, p.Close())
   334  }
   335  
   336  // This verifies that closing the socket aborts a blocking send.
   337  // We use a context because closing the socket also closes pipes
   338  // making it less reproducible.
   339  func TestRespondentCloseContextSend(t *testing.T) {
   340  	s := GetSocket(t, NewSocket)
   341  	p := GetSocket(t, xsurveyor.NewSocket)
   342  	MustSucceed(t, s.SetOption(mangos.OptionWriteQLen, 1))
   343  	MustSucceed(t, p.SetOption(mangos.OptionReadQLen, 1))
   344  	c, e := s.OpenContext()
   345  	MustSucceed(t, e)
   346  
   347  	ConnectPair(t, s, p)
   348  
   349  	MustSucceed(t, c.SetOption(mangos.OptionSendDeadline, time.Millisecond*10))
   350  
   351  	time.Sleep(time.Millisecond * 10)
   352  	cnt := 0
   353  	data := []byte{0x80, 0, 0, 1}
   354  	for i := 0; i < 100; i++ {
   355  		// We have to make a raw message when using xsurveyor.  We
   356  		// use xsurveyor because normal surveyor will simply discard
   357  		// messages for surveys it doesn't have outstanding.
   358  		m := mangos.NewMessage(0)
   359  		m.Header = append(m.Header, data...)
   360  		data[3]++
   361  		MustSucceed(t, p.SendMsg(m))
   362  		m, e := c.RecvMsg()
   363  		MustSucceed(t, e)
   364  		MustNotBeNil(t, m)
   365  		if e = c.SendMsg(m); e != nil {
   366  			MustBeError(t, e, mangos.ErrSendTimeout)
   367  			break
   368  		}
   369  		cnt++
   370  
   371  		// NB: We never ask the peer to receive it -- this ensures we
   372  		// encounter back-pressure.
   373  	}
   374  	MustBeTrue(t, cnt > 0) // Some in-flight sends possible.
   375  	MustBeTrue(t, cnt < 10)
   376  
   377  	m := mangos.NewMessage(0)
   378  	m.Header = append(m.Header, data...)
   379  	data[3]++
   380  	MustSucceed(t, p.SendMsg(m))
   381  
   382  	MustSucceed(t, c.SetOption(mangos.OptionSendDeadline, time.Minute))
   383  	m, e = c.RecvMsg()
   384  	MustSucceed(t, e)
   385  	MustNotBeNil(t, m)
   386  	time.AfterFunc(time.Millisecond*20, func() { MustSucceed(t, c.Close()) })
   387  	MustBeError(t, c.SendMsg(m), mangos.ErrClosed)
   388  
   389  	MustSucceed(t, p.Close())
   390  	MustSucceed(t, s.Close())
   391  }
   392  
   393  func TestRespondentContextClosed(t *testing.T) {
   394  	s := GetSocket(t, NewSocket)
   395  	c, e := s.OpenContext()
   396  	MustSucceed(t, e)
   397  	MustNotBeNil(t, c)
   398  	MustSucceed(t, c.Close())
   399  	MustBeError(t, c.Close(), mangos.ErrClosed)
   400  
   401  	c, e = s.OpenContext()
   402  	MustSucceed(t, e)
   403  	MustNotBeNil(t, c)
   404  
   405  	MustSucceed(t, s.Close())
   406  
   407  	MustBeError(t, c.Close(), mangos.ErrClosed)
   408  
   409  	_, e = s.OpenContext()
   410  	MustBeError(t, e, mangos.ErrClosed)
   411  }
   412  
   413  // This sets up a bunch of contexts to run in parallel, and verifies that
   414  // they all seem to run with no mis-deliveries.
   415  func TestRespondentMultiContexts(t *testing.T) {
   416  	count := 30
   417  	repeat := 20
   418  
   419  	s := GetSocket(t, NewSocket)
   420  	p := GetSocket(t, surveyor.NewSocket)
   421  	MustSucceed(t, s.SetOption(mangos.OptionReadQLen, count*repeat))
   422  	MustSucceed(t, p.SetOption(mangos.OptionReadQLen, count*repeat))
   423  	MustSucceed(t, p.SetOption(mangos.OptionWriteQLen, count*repeat))
   424  	MustSucceed(t, s.SetOption(mangos.OptionWriteQLen, count*repeat))
   425  
   426  	ConnectPair(t, p, s)
   427  
   428  	recv := make([]int, count)
   429  	send := make([]int, count)
   430  
   431  	var wg1 sync.WaitGroup
   432  	fn := func(c1, c2 mangos.Context, index int) {
   433  		defer wg1.Done()
   434  
   435  		topic := make([]byte, 4)
   436  		binary.BigEndian.PutUint32(topic, uint32(index))
   437  
   438  		for i := 0; i < repeat; i++ {
   439  			MustSucceed(t, c2.Send(topic))
   440  			m, e := c1.RecvMsg()
   441  			MustSucceed(t, e)
   442  			MustBeTrue(t, len(m.Body) == 4)
   443  			peer := binary.BigEndian.Uint32(m.Body)
   444  			recv[int(peer)]++
   445  			MustSucceed(t, c1.Send([]byte("answer")))
   446  			m, e = c2.RecvMsg()
   447  			MustSucceed(t, e)
   448  			MustBeTrue(t, string(m.Body) == "answer")
   449  			send[index]++
   450  		}
   451  	}
   452  
   453  	wg1.Add(count)
   454  
   455  	for i := 0; i < count; i++ {
   456  		c1, e := s.OpenContext()
   457  		MustSucceed(t, e)
   458  		MustNotBeNil(t, c1)
   459  
   460  		c2, e := p.OpenContext()
   461  		MustSucceed(t, e)
   462  		MustNotBeNil(t, c2)
   463  
   464  		MustSucceed(t, c1.SetOption(mangos.OptionRecvDeadline, time.Minute/4))
   465  		MustSucceed(t, c1.SetOption(mangos.OptionSendDeadline, time.Minute/4))
   466  		MustSucceed(t, c2.SetOption(mangos.OptionRecvDeadline, time.Minute/4))
   467  		MustSucceed(t, c2.SetOption(mangos.OptionSurveyTime, time.Minute/2))
   468  
   469  		go fn(c1, c2, i)
   470  	}
   471  
   472  	// Give time for everything to be delivered.
   473  	wg1.Wait()
   474  	MustSucceed(t, p.Close())
   475  	MustSucceed(t, s.Close())
   476  
   477  	for i := 0; i < count; i++ {
   478  		MustBeTrue(t, recv[i] == repeat)
   479  		MustBeTrue(t, send[i] == repeat)
   480  	}
   481  }