go.nanomsg.org/mangos/v3@v3.4.3-0.20240217232803-46464076f1f5/protocol/sub/sub_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 sub
    16  
    17  import (
    18  	"math/rand"
    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/protocol/pub"
    27  	_ "go.nanomsg.org/mangos/v3/transport/inproc"
    28  )
    29  
    30  func TestSubIdentity(t *testing.T) {
    31  	id := GetSocket(t, NewSocket).Info()
    32  	MustBeTrue(t, id.Self == ProtoSub)
    33  	MustBeTrue(t, id.Peer == ProtoPub)
    34  	MustBeTrue(t, id.SelfName == "sub")
    35  	MustBeTrue(t, id.PeerName == "pub")
    36  }
    37  
    38  func TestSubCooked(t *testing.T) {
    39  	VerifyCooked(t, NewSocket)
    40  }
    41  
    42  func TestSubNoSend(t *testing.T) {
    43  	CannotSend(t, NewSocket)
    44  }
    45  
    46  func TestSubClosed(t *testing.T) {
    47  	VerifyClosedRecv(t, NewSocket)
    48  	VerifyClosedClose(t, NewSocket)
    49  	VerifyClosedDial(t, NewSocket)
    50  	VerifyClosedListen(t, NewSocket)
    51  	VerifyClosedAddPipe(t, NewSocket)
    52  	VerifyClosedContext(t, NewSocket)
    53  }
    54  
    55  func TestSubOptions(t *testing.T) {
    56  	VerifyInvalidOption(t, NewSocket)
    57  	VerifyOptionDuration(t, NewSocket, OptionRecvDeadline)
    58  	VerifyOptionInt(t, NewSocket, OptionReadQLen)
    59  }
    60  
    61  func TestSubRecvDeadline(t *testing.T) {
    62  	s := GetSocket(t, NewSocket)
    63  	defer MustClose(t, s)
    64  	MustSucceed(t, s.SetOption(OptionRecvDeadline, time.Millisecond))
    65  	MustNotRecv(t, s, ErrRecvTimeout)
    66  }
    67  
    68  func TestSubSubscribe(t *testing.T) {
    69  	s, e := NewSocket()
    70  	MustSucceed(t, e)
    71  	MustNotBeNil(t, s)
    72  	MustSucceed(t, s.SetOption(OptionSubscribe, "topic"))
    73  	MustSucceed(t, s.SetOption(OptionSubscribe, "topic"))
    74  	MustSucceed(t, s.SetOption(OptionSubscribe, []byte{0, 1}))
    75  	MustBeError(t, s.SetOption(OptionSubscribe, 1), ErrBadValue)
    76  
    77  	MustBeError(t, s.SetOption(OptionUnsubscribe, "nope"), ErrBadValue)
    78  	MustSucceed(t, s.SetOption(OptionUnsubscribe, "topic"))
    79  	MustSucceed(t, s.SetOption(OptionUnsubscribe, []byte{0, 1}))
    80  	MustBeError(t, s.SetOption(OptionUnsubscribe, false), ErrBadValue)
    81  	MustSucceed(t, s.Close())
    82  }
    83  
    84  func TestSubUnsubscribeDrops(t *testing.T) {
    85  	s, e := NewSocket()
    86  	MustSucceed(t, e)
    87  	p, e := pub.NewSocket()
    88  	MustSucceed(t, e)
    89  	a := AddrTestInp()
    90  
    91  	MustSucceed(t, s.SetOption(OptionReadQLen, 50))
    92  	MustSucceed(t, p.Listen(a))
    93  	MustSucceed(t, s.Dial(a))
    94  
    95  	time.Sleep(time.Millisecond * 20)
    96  
    97  	MustSucceed(t, s.SetOption(OptionSubscribe, "1"))
    98  	MustSucceed(t, s.SetOption(OptionSubscribe, "2"))
    99  
   100  	for i := 0; i < 10; i++ {
   101  		MustSucceed(t, p.Send([]byte("1")))
   102  		MustSucceed(t, p.Send([]byte("2")))
   103  	}
   104  
   105  	time.Sleep(time.Millisecond * 10)
   106  	MustSucceed(t, s.SetOption(OptionUnsubscribe, "1"))
   107  	for i := 0; i < 10; i++ {
   108  		v, e := s.Recv()
   109  		MustSucceed(t, e)
   110  		MustBeTrue(t, string(v) == "2")
   111  	}
   112  
   113  	MustSucceed(t, p.Close())
   114  	MustSucceed(t, s.Close())
   115  }
   116  
   117  func TestSubUnsubscribeLoad(t *testing.T) {
   118  	s := GetSocket(t, NewSocket)
   119  	p := GetSocket(t, pub.NewSocket)
   120  	MustSucceed(t, s.SetOption(OptionReadQLen, 1))
   121  	MustSucceed(t, p.SetOption(OptionWriteQLen, 100))
   122  
   123  	ConnectPair(t, s, p)
   124  	ConnectPair(t, s, p)
   125  
   126  	MustSucceed(t, s.SetOption(OptionSubscribe, "1"))
   127  	MustSucceed(t, s.SetOption(OptionSubscribe, "2"))
   128  
   129  	for i := 0; i < 10; i++ {
   130  		MustSucceed(t, p.Send([]byte("1")))
   131  		MustSucceed(t, p.Send([]byte("2")))
   132  	}
   133  	var wg sync.WaitGroup
   134  	nPub := 3
   135  	wg.Add(nPub)
   136  	for i := 0; i < nPub; i++ {
   137  		go func() {
   138  			defer wg.Done()
   139  			for {
   140  				var err error
   141  				if err = p.Send([]byte{'2'}); err == ErrClosed {
   142  					return
   143  				}
   144  				MustSucceed(t, err)
   145  			}
   146  		}()
   147  	}
   148  
   149  	time.Sleep(time.Millisecond * 10)
   150  	MustSucceed(t, s.SetOption(OptionUnsubscribe, "1"))
   151  	time.Sleep(time.Millisecond * 50)
   152  	for i := 0; i < 50; i++ {
   153  		MustRecvString(t, s, "2")
   154  	}
   155  
   156  	MustSucceed(t, p.Close())
   157  	MustSucceed(t, s.Close())
   158  	wg.Wait()
   159  }
   160  
   161  func TestSubRecvQLen(t *testing.T) {
   162  	s := GetSocket(t, NewSocket)
   163  	defer MustClose(t, s)
   164  	p := GetSocket(t, pub.NewSocket)
   165  	defer MustClose(t, p)
   166  
   167  	MustSucceed(t, s.SetOption(OptionRecvDeadline, time.Millisecond*10))
   168  	MustSucceed(t, s.SetOption(OptionReadQLen, 2))
   169  	MustSucceed(t, s.SetOption(OptionSubscribe, []byte{}))
   170  
   171  	ConnectPair(t, s, p)
   172  	time.Sleep(time.Millisecond * 50)
   173  
   174  	MustSendString(t, p, "one")
   175  	MustSendString(t, p, "two")
   176  	MustSendString(t, p, "three")
   177  	time.Sleep(time.Millisecond * 50)
   178  
   179  	MustRecvString(t, s, "two")
   180  	MustRecvString(t, s, "three")
   181  	MustNotRecv(t, s, ErrRecvTimeout)
   182  }
   183  
   184  func TestSubRecvQLenResizeDiscard(t *testing.T) {
   185  	s := GetSocket(t, NewSocket)
   186  	p := GetSocket(t, pub.NewSocket)
   187  	MustSucceed(t, s.SetOption(OptionRecvDeadline, time.Millisecond*100))
   188  	MustSucceed(t, s.SetOption(OptionReadQLen, 10))
   189  	MustSucceed(t, s.SetOption(OptionSubscribe, []byte{}))
   190  
   191  	ConnectPair(t, s, p)
   192  
   193  	MustSendString(t, p, "one")
   194  	MustSendString(t, p, "two")
   195  	MustSendString(t, p, "three")
   196  
   197  	// Sleep allows the messages to arrive in the recvQ before we resize.
   198  	time.Sleep(time.Millisecond * 50)
   199  
   200  	// Resize it
   201  	MustSucceed(t, s.SetOption(OptionReadQLen, 20))
   202  
   203  	MustNotRecv(t, s, ErrRecvTimeout)
   204  	MustSucceed(t, p.Close())
   205  	MustSucceed(t, s.Close())
   206  }
   207  
   208  func TestSubRecvResizeContinue(t *testing.T) {
   209  	s := GetSocket(t, NewSocket)
   210  	defer MustClose(t, s)
   211  	p := GetSocket(t, pub.NewSocket)
   212  	defer MustClose(t, p)
   213  
   214  	MustSucceed(t, s.SetOption(OptionRecvDeadline, time.Second*10))
   215  	MustSucceed(t, s.SetOption(OptionReadQLen, 10))
   216  	MustSucceed(t, s.SetOption(OptionSubscribe, []byte{}))
   217  
   218  	ConnectPair(t, s, p)
   219  
   220  	var wg sync.WaitGroup
   221  	pass := false
   222  	wg.Add(1)
   223  	time.AfterFunc(time.Millisecond*50, func() {
   224  		defer wg.Done()
   225  		MustSucceed(t, s.SetOption(OptionReadQLen, 2))
   226  		MustSendString(t, p, "ping")
   227  		pass = true
   228  	})
   229  
   230  	MustRecvString(t, s, "ping")
   231  	wg.Wait()
   232  	MustBeTrue(t, pass)
   233  }
   234  
   235  func TestSubContextOpen(t *testing.T) {
   236  	s, e := NewSocket()
   237  	MustSucceed(t, e)
   238  	c, e := s.OpenContext()
   239  	MustSucceed(t, e)
   240  
   241  	// Also test that we can't send on this.
   242  	MustBeError(t, c.Send([]byte{}), ErrProtoOp)
   243  	MustSucceed(t, c.Close())
   244  	MustSucceed(t, s.Close())
   245  
   246  	MustBeError(t, c.Close(), ErrClosed)
   247  }
   248  
   249  func TestSubSocketCloseContext(t *testing.T) {
   250  	s, e := NewSocket()
   251  	MustSucceed(t, e)
   252  	c, e := s.OpenContext()
   253  	MustSucceed(t, e)
   254  
   255  	MustSucceed(t, s.Close())
   256  
   257  	// Verify that the context is already closed (closing the socket
   258  	// closes the context.)
   259  	MustBeError(t, c.Close(), ErrClosed)
   260  }
   261  
   262  func TestSubMultiContexts(t *testing.T) {
   263  	s, e := NewSocket()
   264  	MustSucceed(t, e)
   265  	MustNotBeNil(t, s)
   266  
   267  	c1, e := s.OpenContext()
   268  	MustSucceed(t, e)
   269  	MustNotBeNil(t, c1)
   270  	c2, e := s.OpenContext()
   271  	MustSucceed(t, e)
   272  	MustNotBeNil(t, c2)
   273  	MustBeTrue(t, c1 != c2)
   274  
   275  	MustSucceed(t, c1.SetOption(mangos.OptionSubscribe, "1"))
   276  	MustSucceed(t, c2.SetOption(mangos.OptionSubscribe, "2"))
   277  	MustSucceed(t, c1.SetOption(mangos.OptionSubscribe, "*"))
   278  	MustSucceed(t, c2.SetOption(mangos.OptionSubscribe, "*"))
   279  
   280  	p, e := pub.NewSocket()
   281  	MustSucceed(t, e)
   282  	MustNotBeNil(t, p)
   283  
   284  	a := AddrTestInp()
   285  
   286  	MustSucceed(t, p.Listen(a))
   287  	MustSucceed(t, s.Dial(a))
   288  
   289  	// Make sure we have dialed properly
   290  	time.Sleep(time.Millisecond * 10)
   291  
   292  	sent := []int{0, 0}
   293  	recv := []int{0, 0}
   294  	wildrecv := []int{0, 0}
   295  	wildsent := 0
   296  	mesg := []string{"1", "2"}
   297  	var wg sync.WaitGroup
   298  	wg.Add(2)
   299  	fn := func(c mangos.Context, index int) {
   300  		for {
   301  			m, e := c.RecvMsg()
   302  			if e == nil {
   303  				switch string(m.Body) {
   304  				case mesg[index]:
   305  					recv[index]++
   306  				case "*":
   307  					wildrecv[index]++
   308  				default:
   309  					MustBeTrue(t, false)
   310  				}
   311  				continue
   312  			}
   313  			MustBeError(t, e, mangos.ErrClosed)
   314  			wg.Done()
   315  			return
   316  		}
   317  	}
   318  
   319  	go fn(c1, 0)
   320  	go fn(c2, 1)
   321  
   322  	rng := rand.NewSource(32)
   323  
   324  	// Choose an odd number so that it does not divide evenly, ensuring
   325  	// that there will be a non-equal distribution.  Note that with our
   326  	// fixed seed above, it works out to 41 & 60.
   327  	for i := 0; i < 101; i++ {
   328  		index := int(rng.Int63() & 1)
   329  		if rng.Int63()&128 < 8 {
   330  			MustSucceed(t, p.Send([]byte{'*'}))
   331  			wildsent++
   332  		} else {
   333  			MustSucceed(t, p.Send([]byte(mesg[index])))
   334  			sent[index]++
   335  		}
   336  	}
   337  
   338  	// Give time for everything to be delivered.
   339  	time.Sleep(time.Millisecond * 50)
   340  	MustSucceed(t, c1.Close())
   341  	MustSucceed(t, c2.Close())
   342  	wg.Wait()
   343  
   344  	MustBeTrue(t, sent[0] == recv[0])
   345  	MustBeTrue(t, sent[1] == recv[1])
   346  	MustBeTrue(t, wildsent == wildrecv[0])
   347  	MustBeTrue(t, wildsent == wildrecv[1])
   348  
   349  	MustSucceed(t, s.Close())
   350  	MustSucceed(t, p.Close())
   351  }