go.nanomsg.org/mangos/v3@v3.4.3-0.20240217232803-46464076f1f5/internal/test/mock.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 test 16 17 import ( 18 "sync" 19 "testing" 20 "time" 21 22 "go.nanomsg.org/mangos/v3" 23 "go.nanomsg.org/mangos/v3/protocol" 24 "go.nanomsg.org/mangos/v3/transport" 25 ) 26 27 // This file implements a mock transport, useful for testing. 28 29 // ListenerDialer does both Listen and Dial. (That is, it is both 30 // a dialer and a listener at once.) 31 type mockCreator struct { 32 pipeQ chan MockPipe 33 closeQ chan struct{} 34 errorQ chan error 35 proto uint16 36 deferClose bool // sometimes we don't want close to really work yet 37 closed bool 38 addr string 39 maxRecvSize int 40 lock sync.Mutex 41 } 42 43 // mockPipe implements a mocked transport.Pipe 44 type mockPipe struct { 45 lProto uint16 46 rProto uint16 47 closeQ chan struct{} 48 recvQ chan *mangos.Message 49 sendQ chan *mangos.Message 50 recvErrQ chan error 51 sendErrQ chan error 52 deferClose bool 53 closed bool 54 lock sync.Mutex 55 initOnce sync.Once 56 } 57 58 func (mp *mockPipe) init() { 59 mp.initOnce.Do(func() { 60 mp.recvQ = make(chan *mangos.Message) 61 mp.sendQ = make(chan *mangos.Message) 62 mp.closeQ = make(chan struct{}) 63 mp.recvErrQ = make(chan error, 1) 64 mp.sendErrQ = make(chan error, 1) 65 }) 66 } 67 68 func (mp *mockPipe) SendQ() <-chan *protocol.Message { 69 mp.init() 70 return mp.sendQ 71 } 72 73 func (mp *mockPipe) RecvQ() chan<- *protocol.Message { 74 mp.init() 75 return mp.recvQ 76 } 77 78 func (mp *mockPipe) InjectSendError(e error) { 79 mp.init() 80 select { 81 case mp.sendErrQ <- e: 82 default: 83 } 84 } 85 86 func (mp *mockPipe) InjectRecvError(e error) { 87 mp.init() 88 select { 89 case mp.recvErrQ <- e: 90 default: 91 } 92 } 93 94 func (mp *mockPipe) Send(m *mangos.Message) error { 95 mp.init() 96 select { 97 case <-mp.closeQ: 98 return mangos.ErrClosed 99 case e := <-mp.sendErrQ: 100 return e 101 case mp.sendQ <- m: 102 return nil 103 } 104 } 105 106 func (mp *mockPipe) Recv() (*mangos.Message, error) { 107 mp.init() 108 select { 109 case <-mp.closeQ: 110 return nil, mangos.ErrClosed 111 case e := <-mp.recvErrQ: 112 return nil, e 113 case m := <-mp.recvQ: 114 return m, nil 115 } 116 } 117 118 func (mp *mockPipe) GetOption(name string) (interface{}, error) { 119 switch name { 120 case mangos.OptionRemoteAddr, mangos.OptionLocalAddr: 121 return "mock://mock", nil 122 } 123 return nil, mangos.ErrBadOption 124 } 125 126 func (mp *mockPipe) Close() error { 127 mp.lock.Lock() 128 defer mp.lock.Unlock() 129 if !mp.closed { 130 mp.closed = true 131 if !mp.deferClose { 132 select { 133 case <-mp.closeQ: 134 default: 135 close(mp.closeQ) 136 } 137 } 138 } 139 return nil 140 } 141 142 func (mp *mockPipe) DeferClose(later bool) { 143 mp.lock.Lock() 144 defer mp.lock.Unlock() 145 mp.deferClose = later 146 if !later && mp.closed { 147 select { 148 case <-mp.closeQ: 149 default: 150 close(mp.closeQ) 151 } 152 } 153 } 154 155 func (mp *mockPipe) MockRecvMsg(d time.Duration) (*protocol.Message, error) { 156 select { 157 case <-time.After(d): 158 return nil, mangos.ErrRecvTimeout 159 case m := <-mp.sendQ: 160 return m, nil 161 case <-mp.closeQ: 162 return nil, mangos.ErrClosed 163 } 164 } 165 166 func (mp *mockPipe) MockSendMsg(m *protocol.Message, d time.Duration) error { 167 select { 168 case <-time.After(d): 169 return mangos.ErrSendTimeout 170 case mp.recvQ <- m: 171 return nil 172 case <-mp.closeQ: 173 return mangos.ErrClosed 174 } 175 } 176 177 // NewMockPipe creates a mocked transport pipe. 178 func NewMockPipe(lProto, rProto uint16) MockPipe { 179 mp := &mockPipe{ 180 lProto: lProto, 181 rProto: rProto, 182 } 183 mp.init() 184 return mp 185 } 186 187 // MockPipe is a mocked transport pipe. 188 type MockPipe interface { 189 // SendQ obtains the send queue. Test code can read from this 190 // to get messages sent by the socket. 191 SendQ() <-chan *protocol.Message 192 193 // RecvQ obtains the recv queue. Test code can write to this 194 // to send message to the socket. 195 RecvQ() chan<- *protocol.Message 196 197 // InjectSendError is used to inject an error that will be seen 198 // by the next Send() operation. 199 InjectSendError(error) 200 201 // InjectRecvError is used to inject an error that will be seen 202 // by the next Recv() operation. 203 InjectRecvError(error) 204 205 // DeferClose defers closing. 206 DeferClose(deferring bool) 207 208 // MockSendMsg lets us inject a message into the queue. 209 MockSendMsg(*protocol.Message, time.Duration) error 210 211 // MockRecvMsg lets us attempt to receive a message. 212 MockRecvMsg(time.Duration) (*protocol.Message, error) 213 214 transport.Pipe 215 } 216 217 // MockCreator is an abstraction of both dialers and listeners, which 218 // allows us to test various transport failure conditions. 219 type MockCreator interface { 220 // NewPipe creates a Pipe, but does not add it. The pipe will 221 // use the assigned peer protocol. 222 NewPipe(peer uint16) MockPipe 223 224 // AddPipe adds the given pipe, returning an error if there is 225 // no room to do so in the pipeQ. 226 AddPipe(pipe MockPipe) error 227 228 // DeferClose is used to defer close operations. If Close() 229 // is called, and deferring is false, then the close 230 // will happen immediately. 231 DeferClose(deferring bool) 232 233 // Close is used to close the creator. 234 Close() error 235 236 // These are methods from transport, for Dialer and Listener. 237 238 // Dial simulates dialing 239 Dial() (transport.Pipe, error) 240 241 // Listen simulates listening 242 Listen() error 243 244 // Accept simulates accepting. 245 Accept() (transport.Pipe, error) 246 247 // GetOption simulates getting an option. 248 GetOption(string) (interface{}, error) 249 250 // SetOption simulates setting an option. 251 SetOption(string, interface{}) error 252 253 // Address returns the address. 254 Address() string 255 256 // InjectError is used to inject a single error. 257 InjectError(error) 258 } 259 260 func (mc *mockCreator) InjectError(e error) { 261 select { 262 case mc.errorQ <- e: 263 default: 264 } 265 } 266 267 func (mc *mockCreator) getPipe() (transport.Pipe, error) { 268 select { 269 case mp := <-mc.pipeQ: 270 return mp, nil 271 case <-mc.closeQ: 272 return nil, mangos.ErrClosed 273 case e := <-mc.errorQ: 274 return nil, e 275 } 276 } 277 278 func (mc *mockCreator) Dial() (transport.Pipe, error) { 279 return mc.getPipe() 280 } 281 282 func (mc *mockCreator) Accept() (transport.Pipe, error) { 283 return mc.getPipe() 284 } 285 286 func (mc *mockCreator) Listen() error { 287 select { 288 case e := <-mc.errorQ: 289 return e 290 case <-mc.closeQ: 291 return mangos.ErrClosed 292 default: 293 return nil 294 } 295 } 296 297 func (mc *mockCreator) SetOption(name string, val interface{}) error { 298 switch name { 299 case "mockError": 300 return val.(error) 301 case mangos.OptionMaxRecvSize: 302 if v, ok := val.(int); ok && v >= 0 { 303 // These are magical values used for test validation. 304 switch v { 305 case 1001: 306 return mangos.ErrBadValue 307 case 1002: 308 return mangos.ErrBadOption 309 } 310 mc.maxRecvSize = v 311 return nil 312 } 313 return mangos.ErrBadValue 314 } 315 return mangos.ErrBadOption 316 } 317 318 func (mc *mockCreator) GetOption(name string) (interface{}, error) { 319 switch name { 320 case "mock": 321 return mc, nil 322 case "mockError": 323 return nil, mangos.ErrProtoState 324 case mangos.OptionMaxRecvSize: 325 return mc.maxRecvSize, nil 326 } 327 return nil, mangos.ErrBadOption 328 } 329 330 // NewPipe just returns a ready pipe with the local peer set up. 331 func (mc *mockCreator) NewPipe(peer uint16) MockPipe { 332 return NewMockPipe(mc.proto, peer) 333 } 334 335 // AddPipe adds a pipe. 336 func (mc *mockCreator) AddPipe(mp MockPipe) error { 337 select { 338 case mc.pipeQ <- mp: 339 return nil 340 default: 341 } 342 return mangos.ErrConnRefused 343 } 344 345 // DeferClose is used to hold off the close to simulate a the endpoint 346 // still creating pipes even after close. It doesn't actually do a close, 347 // but if this is disabled, and Close() was called previously, then the 348 // close will happen immediately. 349 func (mc *mockCreator) DeferClose(b bool) { 350 mc.lock.Lock() 351 defer mc.lock.Unlock() 352 mc.deferClose = b 353 if mc.closed && !mc.deferClose { 354 select { 355 case <-mc.closeQ: 356 default: 357 close(mc.closeQ) 358 } 359 } 360 } 361 362 // Close closes the endpoint, but only if SkipClose is false. 363 func (mc *mockCreator) Close() error { 364 mc.lock.Lock() 365 defer mc.lock.Unlock() 366 mc.closed = true 367 if !mc.deferClose { 368 select { 369 case <-mc.closeQ: 370 default: 371 close(mc.closeQ) 372 } 373 } 374 return nil 375 } 376 377 func (mc *mockCreator) Address() string { 378 return mc.addr 379 } 380 381 type mockTransport struct{} 382 383 func (mockTransport) Scheme() string { 384 return "mock" 385 } 386 387 func (mt mockTransport) newCreator(addr string, sock mangos.Socket) (MockCreator, error) { 388 if _, err := transport.StripScheme(mt, addr); err != nil { 389 return nil, err 390 } 391 mc := &mockCreator{ 392 proto: sock.Info().Self, 393 pipeQ: make(chan MockPipe, 1), 394 closeQ: make(chan struct{}), 395 errorQ: make(chan error, 1), 396 addr: addr, 397 } 398 return mc, nil 399 } 400 401 func (mt mockTransport) NewListener(addr string, sock mangos.Socket) (transport.Listener, error) { 402 return mt.newCreator(addr, sock) 403 } 404 405 func (mt mockTransport) NewDialer(addr string, sock mangos.Socket) (transport.Dialer, error) { 406 return mt.newCreator(addr, sock) 407 } 408 409 // AddMockTransport registers the mock transport. 410 func AddMockTransport() { 411 transport.RegisterTransport(mockTransport{}) 412 } 413 414 // GetMockListener returns a listener that creates mock pipes. 415 func GetMockListener(t *testing.T, s mangos.Socket) (mangos.Listener, MockCreator) { 416 AddMockTransport() 417 l, e := s.NewListener("mock://mock", nil) 418 MustSucceed(t, e) 419 v, e := l.GetOption("mock") 420 MustSucceed(t, e) 421 ml, ok := v.(MockCreator) 422 MustBeTrue(t, ok) 423 return l, ml 424 } 425 426 // GetMockDialer returns a dialer that creates mock pipes. 427 func GetMockDialer(t *testing.T, s mangos.Socket) (mangos.Dialer, MockCreator) { 428 AddMockTransport() 429 d, e := s.NewDialer("mock://mock", nil) 430 MustSucceed(t, e) 431 v, e := d.GetOption("mock") 432 MustSucceed(t, e) 433 ml, ok := v.(MockCreator) 434 MustBeTrue(t, ok) 435 return d, ml 436 } 437 438 // MockAddPipe simulates adding a pipe. 439 func MockAddPipe(t *testing.T, s mangos.Socket, c MockCreator, p MockPipe) mangos.Pipe { 440 var rv mangos.Pipe 441 wg := sync.WaitGroup{} 442 wg.Add(1) 443 hook := s.SetPipeEventHook(func(ev mangos.PipeEvent, pipe mangos.Pipe) { 444 switch ev { 445 case mangos.PipeEventAttached: 446 rv = pipe 447 wg.Done() 448 } 449 }) 450 MustSucceed(t, c.AddPipe(p)) 451 wg.Wait() 452 s.SetPipeEventHook(hook) 453 return rv 454 } 455 456 // MockConnect simulates connecting a pipe. 457 func MockConnect(t *testing.T, s mangos.Socket) (MockPipe, mangos.Pipe) { 458 var pipe mangos.Pipe 459 wg := sync.WaitGroup{} 460 wg.Add(1) 461 462 l, c := GetMockListener(t, s) 463 MustSucceed(t, l.Listen()) 464 465 hook := s.SetPipeEventHook(func(ev mangos.PipeEvent, p mangos.Pipe) { 466 switch ev { 467 case mangos.PipeEventAttached: 468 pipe = p 469 wg.Done() 470 } 471 }) 472 473 mp := c.NewPipe(s.Info().Peer) 474 MustSucceed(t, c.AddPipe(mp)) 475 wg.Wait() 476 s.SetPipeEventHook(hook) 477 return mp, pipe 478 } 479 480 // MockMustSendMsg ensures that the pipe sends a message. 481 func MockMustSendMsg(t *testing.T, p MockPipe, m *mangos.Message, d time.Duration) { 482 MustSucceed(t, p.MockSendMsg(m, d)) 483 } 484 485 // MockMustSend ensures that the pipe sends a message with the body given. 486 func MockMustSend(t *testing.T, p MockPipe, data []byte, d time.Duration) { 487 msg := mangos.NewMessage(0) 488 msg.Body = append(msg.Body, data...) 489 MockMustSendMsg(t, p, msg, d) 490 } 491 492 // MockMustSendStr ensures that the pipe sends a message with a payload 493 // containing the given string. 494 func MockMustSendStr(t *testing.T, p MockPipe, str string, d time.Duration) { 495 msg := mangos.NewMessage(0) 496 msg.Body = []byte(str) 497 MockMustSendMsg(t, p, msg, d) 498 } 499 500 // MockMustRecvStr ensures that the pipe receives a message with the payload 501 // equal to the string. 502 func MockMustRecvStr(t *testing.T, p MockPipe, str string, d time.Duration) { 503 m := mangos.NewMessage(0) 504 m.Body = append(m.Body, []byte(str)...) 505 msg, err := p.MockRecvMsg(d) 506 MustSucceed(t, err) 507 MustBeTrue(t, string(msg.Body) == str) 508 } 509 510 // AddrMock returns a generic address for mock sockets. 511 func AddrMock() string { 512 return "mock://mock" 513 }