go.nanomsg.org/mangos/v3@v3.4.3-0.20240217232803-46464076f1f5/protocol/xstar/xstar_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 xstar
    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"
    26  	_ "go.nanomsg.org/mangos/v3/transport/inproc"
    27  )
    28  
    29  func TestXStarIdentity(t *testing.T) {
    30  	id := MustGetInfo(t, NewSocket)
    31  	MustBeTrue(t, id.Self == ProtoStar)
    32  	MustBeTrue(t, id.SelfName == "star")
    33  	MustBeTrue(t, id.Peer == ProtoStar)
    34  	MustBeTrue(t, id.PeerName == "star")
    35  }
    36  
    37  func TestXStarRaw(t *testing.T) {
    38  	VerifyRaw(t, NewSocket)
    39  }
    40  
    41  func TestXStarClosed(t *testing.T) {
    42  	VerifyClosedRecv(t, NewSocket)
    43  	VerifyClosedSend(t, NewSocket)
    44  	VerifyClosedClose(t, NewSocket)
    45  	VerifyClosedDial(t, NewSocket)
    46  	VerifyClosedListen(t, NewSocket)
    47  	VerifyClosedAddPipe(t, NewSocket)
    48  }
    49  
    50  func TestXStarOptions(t *testing.T) {
    51  	VerifyInvalidOption(t, NewSocket)
    52  	VerifyOptionDuration(t, NewSocket, OptionRecvDeadline)
    53  	VerifyOptionInt(t, NewSocket, OptionReadQLen)
    54  	VerifyOptionInt(t, NewSocket, OptionWriteQLen)
    55  	VerifyOptionInt(t, NewSocket, OptionTTL)
    56  }
    57  
    58  func TestXStarRecvDeadline(t *testing.T) {
    59  	self := GetSocket(t, NewSocket)
    60  	MustSucceed(t, self.SetOption(OptionRecvDeadline, time.Millisecond))
    61  	MustNotRecv(t, self, ErrRecvTimeout)
    62  	MustClose(t, self)
    63  }
    64  
    65  func TestXStarNoHeader(t *testing.T) {
    66  	s, err := NewSocket()
    67  	MustSucceed(t, err)
    68  	defer MustClose(t, s)
    69  
    70  	m := mangos.NewMessage(0)
    71  
    72  	MustSucceed(t, s.SendMsg(m))
    73  }
    74  
    75  func TestXStarTTL(t *testing.T) {
    76  	SetTTLZero(t, NewSocket)
    77  	SetTTLNegative(t, NewSocket)
    78  	SetTTLTooBig(t, NewSocket)
    79  	SetTTLNotInt(t, NewSocket)
    80  	SetTTL(t, NewSocket)
    81  }
    82  
    83  func TestXStarNonBlock(t *testing.T) {
    84  
    85  	self := GetSocket(t, NewSocket)
    86  	defer MustClose(t, self)
    87  
    88  	MustSucceed(t, self.SetOption(OptionWriteQLen, 2))
    89  	MustSucceed(t, self.Listen(AddrTestInp()))
    90  
    91  	start := time.Now()
    92  	for i := 0; i < 100; i++ {
    93  		MustSendString(t, self, "abc")
    94  	}
    95  	end := time.Now()
    96  	MustBeTrue(t, end.Sub(start) < time.Second/10)
    97  }
    98  
    99  func TestXStarSendDrop(t *testing.T) {
   100  	self := GetSocket(t, NewSocket)
   101  	MustSucceed(t, self.SetOption(OptionWriteQLen, 1))
   102  	l, ml := GetMockListener(t, self)
   103  	MustSucceed(t, l.Listen())
   104  	mp := ml.NewPipe(self.Info().Peer)
   105  	MustSucceed(t, ml.AddPipe(mp))
   106  
   107  	for i := 0; i < 100; i++ {
   108  		MustSucceed(t, self.Send([]byte{byte(i)}))
   109  	}
   110  	time.Sleep(time.Millisecond * 10)
   111  	MustSucceed(t, self.Close())
   112  }
   113  
   114  func TestXStarRecvNoHeader(t *testing.T) {
   115  	self := GetSocket(t, NewSocket)
   116  	mock, _ := MockConnect(t, self)
   117  
   118  	MustSucceed(t, self.SetOption(OptionReadQLen, 2))
   119  	MustSucceed(t, self.SetOption(OptionRecvDeadline, time.Millisecond*50))
   120  	MockMustSend(t, mock, []byte{}, time.Second)
   121  	MustNotRecv(t, self, ErrRecvTimeout)
   122  	MustSucceed(t, self.Close())
   123  }
   124  
   125  func TestXStarRecvHeader(t *testing.T) {
   126  	self := GetSocket(t, NewSocket)
   127  	mock, _ := MockConnect(t, self)
   128  
   129  	// First byte is non-zero
   130  	MockMustSendMsg(t, mock, newMessage(0x1000000, "1"), time.Second)
   131  	// Second byte is non-zero
   132  	MockMustSendMsg(t, mock, newMessage(0x10000, "2"), time.Second)
   133  	// Third byte is non-zero
   134  	MockMustSendMsg(t, mock, newMessage(0x100, "3"), time.Second)
   135  	// Fourth gets through
   136  	MockMustSendMsg(t, mock, newMessage(0x1, "4"), time.Second)
   137  
   138  	m := MustRecvMsg(t, self)
   139  	MustBeTrue(t, string(m.Body) == "4")
   140  	MustBeTrue(t, len(m.Header) == 4)
   141  
   142  	// The incoming hop count gets incremented on receive.
   143  	MustBeTrue(t, binary.BigEndian.Uint32(m.Header) == 2)
   144  	MustClose(t, self)
   145  }
   146  
   147  func TestXStarSendRecv(t *testing.T) {
   148  	s1 := GetSocket(t, NewSocket)
   149  	defer MustClose(t, s1)
   150  	s2 := GetSocket(t, NewSocket)
   151  	defer MustClose(t, s2)
   152  
   153  	ConnectPair(t, s1, s2)
   154  
   155  	m := mangos.NewMessage(0)
   156  
   157  	// Write an empty hop count to the header
   158  	m.Header = append(m.Header, 0, 0, 0, 0)
   159  	m.Body = append(m.Body, []byte("test")...)
   160  
   161  	MustSendMsg(t, s1, m)
   162  	m2 := MustRecvMsg(t, s2)
   163  
   164  	MustBeTrue(t, len(m2.Header) == 4)
   165  	MustBeTrue(t, binary.BigEndian.Uint32(m2.Header) == 1)
   166  	MustBeTrue(t, string(m2.Body) == "test")
   167  }
   168  
   169  func TestXStarRedistribute(t *testing.T) {
   170  	s1 := GetSocket(t, NewSocket)
   171  	defer MustClose(t, s1)
   172  	s2 := GetSocket(t, NewSocket)
   173  	defer MustClose(t, s2)
   174  	s3 := GetSocket(t, NewSocket)
   175  
   176  	MustSucceed(t, s1.SetOption(OptionRecvDeadline, time.Millisecond*100))
   177  	MustSucceed(t, s2.SetOption(OptionRecvDeadline, time.Millisecond*100))
   178  	MustSucceed(t, s3.SetOption(OptionRecvDeadline, time.Millisecond*100))
   179  	MustSucceed(t, s1.SetOption(OptionReadQLen, 5))
   180  	MustSucceed(t, s2.SetOption(OptionReadQLen, 5))
   181  	MustSucceed(t, s3.SetOption(OptionReadQLen, 5))
   182  
   183  	ConnectPair(t, s1, s2)
   184  	ConnectPair(t, s3, s2)
   185  
   186  	m := mangos.NewMessage(0)
   187  
   188  	// Write an empty hop count to the header
   189  	m.Header = append(m.Header, 0, 0, 0, 0)
   190  	m.Body = append(m.Body, []byte("test")...)
   191  
   192  	MustSendMsg(t, s1, m)
   193  	m2 := MustRecvMsg(t, s3)
   194  
   195  	MustBeTrue(t, len(m2.Header) == 4)
   196  	// We go through two hops to get here.
   197  	MustBeTrue(t, binary.BigEndian.Uint32(m2.Header) == 2)
   198  	MustBeTrue(t, string(m2.Body) == "test")
   199  
   200  	MustNotRecv(t, s1, mangos.ErrRecvTimeout)
   201  }
   202  
   203  func TestXStarRedistributeBackPressure(t *testing.T) {
   204  	s1 := GetSocket(t, NewSocket)
   205  	defer MustClose(t, s1)
   206  	s2 := GetSocket(t, NewSocket)
   207  	defer MustClose(t, s2)
   208  	s3 := GetSocket(t, NewSocket)
   209  
   210  	MustSucceed(t, s1.SetOption(OptionRecvDeadline, time.Millisecond*100))
   211  	MustSucceed(t, s2.SetOption(OptionRecvDeadline, time.Millisecond*100))
   212  	MustSucceed(t, s3.SetOption(OptionRecvDeadline, time.Millisecond*100))
   213  	MustSucceed(t, s1.SetOption(OptionReadQLen, 5))
   214  	MustSucceed(t, s2.SetOption(OptionReadQLen, 5))
   215  	MustSucceed(t, s3.SetOption(OptionReadQLen, 5))
   216  	MustSucceed(t, s1.SetOption(OptionWriteQLen, 2))
   217  	MustSucceed(t, s3.SetOption(OptionWriteQLen, 2))
   218  	// Make the intermediate queue size too small to force back-pressure.
   219  	MustSucceed(t, s2.SetOption(OptionWriteQLen, 1))
   220  
   221  	ConnectPair(t, s1, s2)
   222  	ConnectPair(t, s3, s2)
   223  
   224  	for i := 0; i < 10; i++ {
   225  		m := mangos.NewMessage(0)
   226  
   227  		// Write an empty hop count to the header
   228  		m.Header = append(m.Header, 0, 0, 0, 0)
   229  		m.Body = append(m.Body, []byte("test")...)
   230  
   231  		MustSendMsg(t, s1, m)
   232  	}
   233  
   234  	m2 := MustRecvMsg(t, s3)
   235  
   236  	MustBeTrue(t, len(m2.Header) == 4)
   237  	// We go through two hops to get here.
   238  	MustBeTrue(t, binary.BigEndian.Uint32(m2.Header) == 2)
   239  	MustBeTrue(t, string(m2.Body) == "test")
   240  
   241  	MustNotRecv(t, s1, ErrRecvTimeout)
   242  }
   243  
   244  // newMessage creates a message as it would come from a pipe.  The
   245  // hops will be part of the body.
   246  func newMessage(hops uint32, content string) *mangos.Message {
   247  	m := mangos.NewMessage(len(content) + 4)
   248  	b := make([]byte, 4)
   249  	binary.BigEndian.PutUint32(b[:4], hops)
   250  	// Requests (coming in) will be entirely on the body.
   251  	m.Body = append(m.Body, b...)
   252  	m.Body = append(m.Body, []byte(content)...)
   253  	return m
   254  }
   255  
   256  func TestXStarRecvPipeAbort(t *testing.T) {
   257  	self := GetSocket(t, NewSocket)
   258  	mp, p := MockConnect(t, self)
   259  
   260  	wg := sync.WaitGroup{}
   261  	wg.Add(1)
   262  
   263  	closeQ := make(chan struct{})
   264  
   265  	go func() {
   266  		defer wg.Done()
   267  		for {
   268  			m := newMessage(1, "")
   269  			select {
   270  			case mp.RecvQ() <- m:
   271  			case <-closeQ:
   272  				return
   273  			}
   274  		}
   275  	}()
   276  
   277  	time.Sleep(time.Millisecond * 20)
   278  	MustSucceed(t, p.Close())
   279  	time.Sleep(time.Millisecond * 20)
   280  	MustSucceed(t, self.Close())
   281  
   282  	close(closeQ)
   283  	wg.Wait()
   284  }
   285  
   286  func TestXStarRecvSockAbort(t *testing.T) {
   287  	self := GetSocket(t, NewSocket)
   288  	l, mc := GetMockListener(t, self)
   289  
   290  	MustSucceed(t, l.Listen())
   291  	mp := mc.NewPipe(self.Info().Peer)
   292  	mp.DeferClose(true)
   293  	MockAddPipe(t, self, mc, mp)
   294  
   295  	wg := sync.WaitGroup{}
   296  	wg.Add(1)
   297  
   298  	closeQ := make(chan struct{})
   299  
   300  	go func() {
   301  		defer wg.Done()
   302  		for {
   303  			m := newMessage(1, "")
   304  			select {
   305  			case mp.RecvQ() <- m:
   306  			case <-closeQ:
   307  				return
   308  			}
   309  		}
   310  	}()
   311  
   312  	time.Sleep(time.Millisecond * 20)
   313  	MustSucceed(t, self.Close())
   314  	mp.DeferClose(true)
   315  	close(closeQ)
   316  	wg.Wait()
   317  }
   318  
   319  func TestXStarBackPressure(t *testing.T) {
   320  	self := GetSocket(t, NewSocket)
   321  	MustSucceed(t, self.SetOption(OptionWriteQLen, 5))
   322  	MockConnect(t, self)
   323  	MockConnect(t, self)
   324  	m := mangos.NewMessage(0)
   325  	m.Header = append(m.Header, 0, 0, 0, 0)
   326  	m.Body = append(m.Body, 42)
   327  	// We don't read at all from it.
   328  	for i := 0; i < 100; i++ {
   329  		MustSendMsg(t, self, m.Dup())
   330  	}
   331  	time.Sleep(time.Millisecond * 100)
   332  	MustClose(t, self)
   333  }