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  }