go.nanomsg.org/mangos/v3@v3.4.3-0.20240217232803-46464076f1f5/internal/test/mocksock.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 "time" 20 21 "go.nanomsg.org/mangos/v3/protocol" 22 ) 23 24 // This file implements a mock socket, useful for testing. 25 26 // mockSock is a dumb pass through mock of a socket. 27 type mockSock struct { 28 recvQ chan *protocol.Message 29 sendQ chan *protocol.Message 30 closeQ chan struct{} 31 proto uint16 32 peer uint16 33 name string 34 peerName string 35 sendExpire time.Duration 36 recvExpire time.Duration 37 once sync.Once 38 lock sync.Mutex 39 raw interface{} 40 rawError error 41 } 42 43 func (s *mockSock) Close() error { 44 select { 45 case <-s.closeQ: 46 return protocol.ErrClosed 47 default: 48 s.once.Do(func() { 49 close(s.closeQ) 50 }) 51 return nil 52 } 53 } 54 55 func (s *mockSock) SendMsg(m *protocol.Message) error { 56 var timerQ <-chan time.Time 57 s.lock.Lock() 58 if exp := s.sendExpire; exp > 0 { 59 timerQ = time.After(exp) 60 } 61 s.lock.Unlock() 62 select { 63 case s.sendQ <- m: 64 return nil 65 case <-s.closeQ: 66 return protocol.ErrClosed 67 case <-timerQ: 68 return protocol.ErrSendTimeout 69 } 70 } 71 72 func (s *mockSock) RecvMsg() (*protocol.Message, error) { 73 var timerQ <-chan time.Time 74 s.lock.Lock() 75 if exp := s.recvExpire; exp > 0 { 76 timerQ = time.After(exp) 77 } 78 s.lock.Unlock() 79 select { 80 case m := <-s.recvQ: 81 return m, nil 82 case <-s.closeQ: 83 return nil, protocol.ErrClosed 84 case <-timerQ: 85 return nil, protocol.ErrRecvTimeout 86 } 87 } 88 89 func (s *mockSock) GetOption(name string) (interface{}, error) { 90 s.lock.Lock() 91 defer s.lock.Unlock() 92 switch name { 93 case protocol.OptionRecvDeadline: 94 return s.recvExpire, nil 95 case protocol.OptionSendDeadline: 96 return s.sendExpire, nil 97 case protocol.OptionRaw: 98 return s.raw, s.rawError 99 } 100 return nil, protocol.ErrBadOption 101 } 102 103 func (s *mockSock) SetOption(name string, val interface{}) error { 104 s.lock.Lock() 105 defer s.lock.Unlock() 106 switch name { 107 case protocol.OptionRecvDeadline: 108 if d, ok := val.(time.Duration); ok { 109 s.recvExpire = d 110 return nil 111 } 112 return protocol.ErrBadValue 113 case protocol.OptionSendDeadline: 114 if d, ok := val.(time.Duration); ok { 115 s.sendExpire = d 116 return nil 117 } 118 return protocol.ErrBadValue 119 } 120 return protocol.ErrBadOption 121 } 122 123 func (s *mockSock) Info() protocol.Info { 124 return protocol.Info{ 125 Self: s.proto, 126 Peer: s.peer, 127 SelfName: s.name, 128 PeerName: s.peerName, 129 } 130 } 131 132 type mockSockPipe struct { 133 p protocol.Pipe 134 closeQ chan struct{} 135 once sync.Once 136 } 137 138 func (p *mockSockPipe) close() { 139 p.once.Do(func() { 140 _ = p.p.Close() 141 close(p.closeQ) 142 }) 143 } 144 145 func (s *mockSock) sender(p *mockSockPipe) { 146 for { 147 select { 148 case m := <-s.sendQ: 149 if p.p.SendMsg(m) != nil { 150 p.close() 151 return 152 } 153 case <-p.closeQ: 154 return 155 } 156 } 157 } 158 159 func (s *mockSock) receiver(p *mockSockPipe) { 160 for { 161 m := p.p.RecvMsg() 162 if m == nil { 163 p.close() 164 return 165 } 166 select { 167 case s.recvQ <- m: 168 case <-p.closeQ: 169 return 170 } 171 } 172 } 173 174 func (s *mockSock) AddPipe(pp protocol.Pipe) error { 175 p := &mockSockPipe{ 176 p: pp, 177 closeQ: make(chan struct{}), 178 } 179 pp.SetPrivate(p) 180 go s.sender(p) 181 go s.receiver(p) 182 return nil 183 } 184 185 func (*mockSock) RemovePipe(pp protocol.Pipe) { 186 p := pp.GetPrivate().(*mockSockPipe) 187 p.close() 188 } 189 190 func (*mockSock) OpenContext() (protocol.Context, error) { 191 return nil, protocol.ErrProtoOp 192 } 193 194 // GetMockSocket returns a mock socket. 195 func GetMockSocket() protocol.Socket { 196 return protocol.MakeSocket(&mockSock{ 197 recvQ: make(chan *protocol.Message, 1), 198 sendQ: make(chan *protocol.Message, 1), 199 closeQ: make(chan struct{}), 200 proto: 1, 201 peer: 1, 202 name: "mockSock", 203 peerName: "mockSock", 204 }) 205 } 206 207 // GetMockSocketRaw is an extended form to get a mocked socket, with particular 208 // properties set (including the response to GetOption() for Raw.) 209 func GetMockSocketRaw(proto, peer uint16, name, peerName string, raw interface{}, err error) protocol.Socket { 210 return protocol.MakeSocket(&mockSock{ 211 recvQ: make(chan *protocol.Message, 1), 212 sendQ: make(chan *protocol.Message, 1), 213 closeQ: make(chan struct{}), 214 proto: proto, 215 peer: peer, 216 name: name, 217 peerName: peerName, 218 raw: raw, 219 rawError: err, 220 }) 221 } 222 223 // NewMockSocket returns a mock socket, and nil. 224 func NewMockSocket() (protocol.Socket, error) { 225 return GetMockSocket(), nil 226 } 227 228 // GetMockSocketEx returns a socket for a specific protocol. 229 func GetMockSocketEx(proto uint16, name string) protocol.Socket { 230 return protocol.MakeSocket(&mockSock{ 231 recvQ: make(chan *protocol.Message, 1), 232 sendQ: make(chan *protocol.Message, 1), 233 closeQ: make(chan struct{}), 234 proto: proto, 235 peer: proto, 236 name: name, 237 peerName: name, 238 }) 239 }