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 }