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 }