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