go.nanomsg.org/mangos/v3@v3.4.3-0.20240217232803-46464076f1f5/protocol/rep/rep_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 rep 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/req" 26 "go.nanomsg.org/mangos/v3/protocol/xrep" 27 "go.nanomsg.org/mangos/v3/protocol/xreq" 28 _ "go.nanomsg.org/mangos/v3/transport/inproc" 29 ) 30 31 func TestRepIdentity(t *testing.T) { 32 s := GetSocket(t, NewSocket) 33 id := s.Info() 34 MustBeTrue(t, id.Self == mangos.ProtoRep) 35 MustBeTrue(t, id.SelfName == "rep") 36 MustBeTrue(t, id.Peer == mangos.ProtoReq) 37 MustBeTrue(t, id.PeerName == "req") 38 MustSucceed(t, s.Close()) 39 } 40 41 func TestRepCooked(t *testing.T) { 42 VerifyCooked(t, NewSocket) 43 } 44 45 func TestRepOptions(t *testing.T) { 46 VerifyInvalidOption(t, NewSocket) 47 VerifyOptionQLen(t, NewSocket, mangos.OptionWriteQLen) 48 VerifyOptionDuration(t, NewSocket, mangos.OptionRecvDeadline) 49 VerifyOptionDuration(t, NewSocket, mangos.OptionSendDeadline) 50 VerifyOptionBool(t, NewSocket, mangos.OptionBestEffort) 51 VerifyOptionInt(t, NewSocket, mangos.OptionTTL) 52 } 53 54 func TestRepClosed(t *testing.T) { 55 VerifyClosedRecv(t, NewSocket) 56 VerifyClosedSend(t, NewSocket) 57 VerifyClosedClose(t, NewSocket) 58 VerifyClosedDial(t, NewSocket) 59 VerifyClosedListen(t, NewSocket) 60 VerifyClosedAddPipe(t, NewSocket) 61 VerifyClosedContext(t, NewSocket) 62 } 63 64 func TestRepTTLZero(t *testing.T) { 65 SetTTLZero(t, NewSocket) 66 } 67 68 func TestRepTTLNegative(t *testing.T) { 69 SetTTLNegative(t, NewSocket) 70 } 71 72 func TestRepTTLTooBig(t *testing.T) { 73 SetTTLTooBig(t, NewSocket) 74 } 75 76 func TestRepTTLSet(t *testing.T) { 77 SetTTL(t, NewSocket) 78 } 79 80 func TestRepTTLDrop(t *testing.T) { 81 TTLDropTest(t, req.NewSocket, NewSocket, xreq.NewSocket, xrep.NewSocket) 82 } 83 84 func TestRepCloseRecv(t *testing.T) { 85 self := GetSocket(t, NewSocket) 86 87 time.AfterFunc(time.Millisecond*20, func() { 88 MustSucceed(t, self.Close()) 89 }) 90 MustNotRecv(t, self, mangos.ErrClosed) 91 } 92 93 func TestRepSendState(t *testing.T) { 94 s := GetSocket(t, NewSocket) 95 MustBeError(t, s.Send([]byte{}), mangos.ErrProtoState) 96 MustSucceed(t, s.Close()) 97 } 98 99 func TestRepBestEffortSend(t *testing.T) { 100 s := GetSocket(t, NewSocket) 101 p := GetSocket(t, xreq.NewSocket) 102 MustSucceed(t, s.SetOption(mangos.OptionWriteQLen, 1)) 103 MustSucceed(t, p.SetOption(mangos.OptionReadQLen, 1)) 104 MustSucceed(t, s.SetOption(mangos.OptionBestEffort, true)) 105 106 ConnectPair(t, s, p) 107 for i := 0; i < 100; i++ { 108 // We have to make a raw message when using xreq. We 109 // use xreq because normal req will simply discard 110 // messages for requests it doesn't have outstanding. 111 m := mangos.NewMessage(0) 112 m.Header = make([]byte, 4) 113 binary.BigEndian.PutUint32(m.Header, uint32(i)|0x80000000) 114 MustSucceed(t, p.SendMsg(m)) 115 m = MustRecvMsg(t, s) 116 MustSucceed(t, s.SendMsg(m)) 117 // NB: We never ask the peer to receive it -- this ensures we 118 // encounter back-pressure. 119 } 120 MustSucceed(t, s.Close()) 121 MustSucceed(t, p.Close()) 122 } 123 124 // This verifies that closing the socket aborts a blocking send. 125 // We use a context because closing the socket also closes pipes 126 // making it less reproducible. 127 func TestRepCloseContextSend(t *testing.T) { 128 self := GetSocket(t, NewSocket) 129 peer := GetSocket(t, xreq.NewSocket) 130 MustSucceed(t, self.SetOption(mangos.OptionWriteQLen, 1)) 131 MustSucceed(t, peer.SetOption(mangos.OptionReadQLen, 1)) 132 c, e := self.OpenContext() 133 MustSucceed(t, e) 134 135 ConnectPair(t, self, peer) 136 137 MustSucceed(t, c.SetOption(mangos.OptionSendDeadline, time.Millisecond*10)) 138 139 time.Sleep(time.Millisecond * 10) 140 cnt := 0 141 data := []byte{0x80, 0, 0, 1} 142 for i := 0; i < 100; i++ { 143 // We have to make a raw message when using xreq. 144 m := mangos.NewMessage(0) 145 m.Header = append(m.Header, data...) 146 data[3]++ 147 MustSucceed(t, peer.SendMsg(m)) 148 m, e := c.RecvMsg() 149 MustSucceed(t, e) 150 MustNotBeNil(t, m) 151 if e = c.SendMsg(m); e != nil { 152 MustBeError(t, e, mangos.ErrSendTimeout) 153 break 154 } 155 cnt++ 156 157 // NB: We never ask the peer to receive it -- this ensures we 158 // encounter back-pressure. 159 } 160 MustBeTrue(t, cnt > 0) // Some in-flight sends possible. 161 MustBeTrue(t, cnt < 10) 162 163 m := mangos.NewMessage(0) 164 m.Header = append(m.Header, data...) 165 data[3]++ 166 MustSucceed(t, peer.SendMsg(m)) 167 168 MustSucceed(t, c.SetOption(mangos.OptionSendDeadline, time.Minute)) 169 m, e = c.RecvMsg() 170 MustSucceed(t, e) 171 MustNotBeNil(t, m) 172 time.AfterFunc(time.Millisecond*20, func() { MustSucceed(t, c.Close()) }) 173 MustBeError(t, c.SendMsg(m), mangos.ErrClosed) 174 175 MustSucceed(t, peer.Close()) 176 MustSucceed(t, self.Close()) 177 } 178 179 func TestRepRecvJunk(t *testing.T) { 180 self := GetSocket(t, NewSocket) 181 mock, _ := MockConnect(t, self) 182 183 // Absent header... 184 MockMustSendStr(t, mock, "", time.Second) 185 186 // Absent request id... (must have bit 31 set) 187 MockMustSend(t, mock, []byte{1, 2, 3, 4}, time.Second) 188 189 time.Sleep(time.Millisecond * 10) 190 MustSucceed(t, self.Close()) 191 } 192 193 func TestRepDoubleRecv(t *testing.T) { 194 self := GetSocket(t, NewSocket) 195 MustSucceed(t, self.SetOption(mangos.OptionRecvDeadline, time.Second)) 196 pass := false 197 var wg sync.WaitGroup 198 wg.Add(1) 199 go func() { 200 defer wg.Done() 201 time.Sleep(time.Millisecond) 202 MustNotRecv(t, self, mangos.ErrProtoState) 203 MustSucceed(t, self.Close()) 204 pass = true 205 }() 206 MustNotRecv(t, self, mangos.ErrClosed) 207 wg.Wait() 208 MustBeTrue(t, pass) 209 } 210 211 func TestRepClosedReply(t *testing.T) { 212 self := GetSocket(t, NewSocket) 213 peer := GetSocket(t, req.NewSocket) 214 ConnectPair(t, self, peer) 215 216 MustSendString(t, peer, "ping") 217 MustRecvString(t, self, "ping") 218 MustSucceed(t, peer.Close()) 219 time.Sleep(time.Millisecond * 20) 220 MustSendString(t, self, "nack") 221 time.Sleep(time.Millisecond * 20) 222 MustSucceed(t, self.Close()) 223 } 224 225 // This exercises the discard of inbound messages waiting, when the pipe 226 // or socket is closed. 227 func TestRepPipeRecvClosePipe(t *testing.T) { 228 self := GetSocket(t, NewSocket) 229 peer := GetSocket(t, req.NewSocket) 230 MustSucceed(t, peer.SetOption(mangos.OptionSendDeadline, time.Millisecond*10)) 231 ConnectPair(t, self, peer) 232 233 MustSendString(t, peer, "") 234 m := MustRecvMsg(t, self) 235 236 for i := 0; i < 10; i++ { 237 e := peer.Send([]byte{}) 238 if e != nil { 239 MustBeError(t, e, mangos.ErrSendTimeout) 240 break 241 } 242 } 243 244 MustSucceed(t, m.Pipe.Close()) 245 time.Sleep(time.Millisecond * 10) 246 MustSucceed(t, peer.Close()) 247 MustSucceed(t, self.Close()) 248 } 249 250 func TestRepPipeRecvCloseSocket(t *testing.T) { 251 self := GetSocket(t, NewSocket) 252 peer := GetSocket(t, req.NewSocket) 253 MustSucceed(t, peer.SetOption(mangos.OptionSendDeadline, time.Millisecond*10)) 254 ConnectPair(t, self, peer) 255 256 for i := 0; i < 10; i++ { 257 e := peer.Send([]byte{}) 258 if e != nil { 259 MustBeError(t, e, mangos.ErrSendTimeout) 260 break 261 } 262 } 263 264 MustSucceed(t, self.Close()) 265 } 266 267 // This sets up a bunch of contexts to run in parallel, and verifies that 268 // they all seem to run with no mis-deliveries. 269 func TestRespondentMultiContexts(t *testing.T) { 270 count := 30 271 repeat := 20 272 273 s := GetSocket(t, NewSocket) 274 p := GetSocket(t, req.NewSocket) 275 276 ConnectPair(t, p, s) 277 278 recv := make([]int, count) 279 send := make([]int, count) 280 281 var wg1 sync.WaitGroup 282 fn := func(c1, c2 mangos.Context, index int) { 283 defer wg1.Done() 284 285 topic := make([]byte, 4) 286 binary.BigEndian.PutUint32(topic, uint32(index)) 287 288 for i := 0; i < repeat; i++ { 289 MustSucceed(t, c2.Send(topic)) 290 m, e := c1.RecvMsg() 291 MustSucceed(t, e) 292 MustBeTrue(t, len(m.Body) == 4) 293 peer := binary.BigEndian.Uint32(m.Body) 294 recv[int(peer)]++ 295 MustSucceed(t, c1.Send([]byte("answer"))) 296 b, e := c2.Recv() 297 MustSucceed(t, e) 298 MustBeTrue(t, string(b) == "answer") 299 send[index]++ 300 } 301 } 302 303 wg1.Add(count) 304 305 for i := 0; i < count; i++ { 306 c1, e := s.OpenContext() 307 MustSucceed(t, e) 308 MustNotBeNil(t, c1) 309 310 c2, e := p.OpenContext() 311 MustSucceed(t, e) 312 MustNotBeNil(t, c2) 313 314 MustSucceed(t, c1.SetOption(mangos.OptionRecvDeadline, time.Minute/4)) 315 MustSucceed(t, c1.SetOption(mangos.OptionSendDeadline, time.Minute/4)) 316 MustSucceed(t, c2.SetOption(mangos.OptionSendDeadline, time.Minute/4)) 317 MustSucceed(t, c2.SetOption(mangos.OptionRecvDeadline, time.Minute/4)) 318 319 go fn(c1, c2, i) 320 } 321 322 // Give time for everything to be delivered. 323 wg1.Wait() 324 MustSucceed(t, p.Close()) 325 MustSucceed(t, s.Close()) 326 327 for i := 0; i < count; i++ { 328 MustBeTrue(t, recv[i] == repeat) 329 MustBeTrue(t, send[i] == repeat) 330 } 331 }