Removes unnecessary returned func from WaitReady
[govpp.git] / adapter / mock / mock_adapter.go
1 // Copyright (c) 2017 Cisco and/or its affiliates.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this 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 mock is an alternative VPP adapter aimed for unit/integration testing where the
16 // actual communication with VPP is not demanded.
17 package mock
18
19 import (
20         "bytes"
21         "log"
22         "reflect"
23         "sync"
24
25         "github.com/lunixbochs/struc"
26
27         "git.fd.io/govpp.git/adapter"
28         "git.fd.io/govpp.git/adapter/mock/binapi"
29         "git.fd.io/govpp.git/api"
30 )
31
32 // VppAdapter represents a mock VPP adapter that can be used for unit/integration testing instead of the vppapiclient adapter.
33 type VppAdapter struct {
34         callback func(context uint32, msgId uint16, data []byte)
35
36         msgNameToIds *map[string]uint16
37         msgIDsToName *map[uint16]string
38         msgIDSeq     uint16
39         binAPITypes  map[string]reflect.Type
40         access       sync.RWMutex
41 }
42
43 // replyHeader represents a common header of each VPP request message.
44 type requestHeader struct {
45         VlMsgID     uint16
46         ClientIndex uint32
47         Context     uint32
48 }
49
50 // replyHeader represents a common header of each VPP reply message.
51 type replyHeader struct {
52         VlMsgID uint16
53         Context uint32
54 }
55
56 // otherHeader represents a common header of each VPP reply message.
57 type otherHeader struct {
58         VlMsgID uint16
59 }
60
61 // defaultReply is a default reply message that mock adapter returns for a request.
62 type defaultReply struct {
63         Retval int32
64 }
65
66 // MessageDTO is a structure used for propagating information to ReplyHandlers.
67 type MessageDTO struct {
68         MsgID    uint16
69         MsgName  string
70         ClientID uint32
71         Data     []byte
72 }
73
74 // ReplyHandler is a type that allows to extend the behaviour of VPP mock.
75 // Return value ok is used to signalize that mock reply is calculated and ready to be used.
76 type ReplyHandler func(request MessageDTO) (reply []byte, msgID uint16, ok bool)
77
78 const (
79         defaultReplyMsgID = 1 // default message ID for the reply to be sent back via callback
80 )
81
82 var replies []api.Message        // FIFO queue of messages
83 var replyHandlers []ReplyHandler // callbacks that are able to calculate mock responses
84 var repliesLock sync.Mutex       // mutex for the queue
85 var mode = 0                     // mode in which the mock operates
86
87 const useRepliesQueue = 1  // use replies in the queue instead of the default one
88 const useReplyHandlers = 2 //use ReplyHandler
89
90 // NewVppAdapter returns a new mock adapter.
91 func NewVppAdapter() adapter.VppAdapter {
92         return &VppAdapter{}
93 }
94
95 // Connect emulates connecting the process to VPP.
96 func (a *VppAdapter) Connect() error {
97         return nil
98 }
99
100 // Disconnect emulates disconnecting the process from VPP.
101 func (a *VppAdapter) Disconnect() {
102         // no op
103 }
104
105 // GetMsgNameByID returns message name for specified message ID.
106 func (a *VppAdapter) GetMsgNameByID(msgID uint16) (string, bool) {
107         switch msgID {
108         case 100:
109                 return "control_ping", true
110         case 101:
111                 return "control_ping_reply", true
112         case 200:
113                 return "sw_interface_dump", true
114         case 201:
115                 return "sw_interface_details", true
116         }
117
118         a.access.Lock()
119         defer a.access.Unlock()
120         a.initMaps()
121         msgName, found := (*a.msgIDsToName)[msgID]
122
123         return msgName, found
124 }
125
126 // RegisterBinAPITypes registers binary API message types in the mock adapter.
127 func (a *VppAdapter) RegisterBinAPITypes(binAPITypes map[string]reflect.Type) {
128         a.access.Lock()
129         defer a.access.Unlock()
130         a.initMaps()
131         for _, v := range binAPITypes {
132                 if msg, ok := reflect.New(v).Interface().(api.Message); ok {
133                         a.binAPITypes[msg.GetMessageName()] = v
134                 }
135         }
136 }
137
138 // ReplyTypeFor returns reply message type for given request message name.
139 func (a *VppAdapter) ReplyTypeFor(requestMsgName string) (reflect.Type, uint16, bool) {
140         replyName, foundName := binapi.ReplyNameFor(requestMsgName)
141         if foundName {
142                 if reply, found := a.binAPITypes[replyName]; found {
143                         msgID, err := a.GetMsgID(replyName, "")
144                         if err == nil {
145                                 return reply, msgID, found
146                         }
147                 }
148         }
149
150         return nil, 0, false
151 }
152
153 // ReplyFor returns reply message for given request message name.
154 func (a *VppAdapter) ReplyFor(requestMsgName string) (api.Message, uint16, bool) {
155         replType, msgID, foundReplType := a.ReplyTypeFor(requestMsgName)
156         if foundReplType {
157                 msgVal := reflect.New(replType)
158                 if msg, ok := msgVal.Interface().(api.Message); ok {
159                         log.Println("FFF ", replType, msgID, foundReplType)
160                         return msg, msgID, true
161                 }
162         }
163
164         return nil, 0, false
165 }
166
167 // ReplyBytes encodes the mocked reply into binary format.
168 func (a *VppAdapter) ReplyBytes(request MessageDTO, reply api.Message) ([]byte, error) {
169         replyMsgID, err := a.GetMsgID(reply.GetMessageName(), reply.GetCrcString())
170         if err != nil {
171                 log.Println("ReplyBytesE ", replyMsgID, " ", reply.GetMessageName(), " clientId: ", request.ClientID,
172                         " ", err)
173                 return nil, err
174         }
175         log.Println("ReplyBytes ", replyMsgID, " ", reply.GetMessageName(), " clientId: ", request.ClientID)
176
177         buf := new(bytes.Buffer)
178         struc.Pack(buf, &replyHeader{VlMsgID: replyMsgID, Context: request.ClientID})
179         struc.Pack(buf, reply)
180
181         return buf.Bytes(), nil
182 }
183
184 // GetMsgID returns mocked message ID for the given message name and CRC.
185 func (a *VppAdapter) GetMsgID(msgName string, msgCrc string) (uint16, error) {
186         switch msgName {
187         case "control_ping":
188                 return 100, nil
189         case "control_ping_reply":
190                 return 101, nil
191         case "sw_interface_dump":
192                 return 200, nil
193         case "sw_interface_details":
194                 return 201, nil
195         }
196
197         a.access.Lock()
198         defer a.access.Unlock()
199         a.initMaps()
200
201         msgID, found := (*a.msgNameToIds)[msgName]
202         if found {
203                 return msgID, nil
204         }
205
206         a.msgIDSeq++
207         msgID = a.msgIDSeq
208         (*a.msgNameToIds)[msgName] = msgID
209         (*a.msgIDsToName)[msgID] = msgName
210
211         log.Println("VPP GetMessageId ", msgID, " name:", msgName, " crc:", msgCrc)
212
213         return msgID, nil
214 }
215
216 // initMaps initializes internal maps (if not already initialized).
217 func (a *VppAdapter) initMaps() {
218         if a.msgIDsToName == nil {
219                 a.msgIDsToName = &map[uint16]string{}
220                 a.msgNameToIds = &map[string]uint16{}
221                 a.msgIDSeq = 1000
222         }
223
224         if a.binAPITypes == nil {
225                 a.binAPITypes = map[string]reflect.Type{}
226         }
227 }
228
229 // SendMsg emulates sending a binary-encoded message to VPP.
230 func (a *VppAdapter) SendMsg(clientID uint32, data []byte) error {
231         switch mode {
232         case useReplyHandlers:
233                 a.initMaps()
234                 for i := len(replyHandlers) - 1; i >= 0; i-- {
235                         replyHandler := replyHandlers[i]
236
237                         buf := bytes.NewReader(data)
238                         reqHeader := requestHeader{}
239                         struc.Unpack(buf, &reqHeader)
240
241                         a.access.Lock()
242                         reqMsgName, _ := (*a.msgIDsToName)[reqHeader.VlMsgID]
243                         a.access.Unlock()
244
245                         reply, msgID, finished := replyHandler(MessageDTO{reqHeader.VlMsgID, reqMsgName,
246                                 clientID, data})
247                         if finished {
248                                 a.callback(clientID, msgID, reply)
249                                 return nil
250                         }
251                 }
252                 fallthrough
253         case useRepliesQueue:
254                 repliesLock.Lock()
255                 defer repliesLock.Unlock()
256
257                 // pop all replies from queue
258                 for i, reply := range replies {
259                         if i > 0 && reply.GetMessageName() == "control_ping_reply" {
260                                 // hack - do not send control_ping_reply immediately, leave it for the the next callback
261                                 replies = []api.Message{}
262                                 replies = append(replies, reply)
263                                 return nil
264                         }
265                         msgID, _ := a.GetMsgID(reply.GetMessageName(), reply.GetCrcString())
266                         buf := new(bytes.Buffer)
267                         if reply.GetMessageType() == api.ReplyMessage {
268                                 struc.Pack(buf, &replyHeader{VlMsgID: msgID, Context: clientID})
269                         } else {
270                                 struc.Pack(buf, &requestHeader{VlMsgID: msgID, Context: clientID})
271                         }
272                         struc.Pack(buf, reply)
273                         a.callback(clientID, msgID, buf.Bytes())
274                 }
275                 if len(replies) > 0 {
276                         replies = []api.Message{}
277                         return nil
278                 }
279
280                 //fallthrough
281         default:
282                 // return default reply
283                 buf := new(bytes.Buffer)
284                 msgID := uint16(defaultReplyMsgID)
285                 struc.Pack(buf, &replyHeader{VlMsgID: msgID, Context: clientID})
286                 struc.Pack(buf, &defaultReply{})
287                 a.callback(clientID, msgID, buf.Bytes())
288         }
289         return nil
290 }
291
292 // SetMsgCallback sets a callback function that will be called by the adapter whenever a message comes from the mock.
293 func (a *VppAdapter) SetMsgCallback(cb func(context uint32, msgID uint16, data []byte)) {
294         a.callback = cb
295 }
296
297 // WaitReady mocks waiting for VPP
298 func (a *VppAdapter) WaitReady() error {
299         return nil
300 }
301
302 // MockReply stores a message to be returned when the next request comes. It is a FIFO queue - multiple replies
303 // can be pushed into it, the first one will be popped when some request comes.
304 // Using of this method automatically switches the mock into th useRepliesQueue mode.
305 func (a *VppAdapter) MockReply(msg api.Message) {
306         repliesLock.Lock()
307         defer repliesLock.Unlock()
308
309         replies = append(replies, msg)
310         mode = useRepliesQueue
311 }
312
313 // MockReplyHandler registers a handler function that is supposed to generate mock responses to incoming requests.
314 // Using of this method automatically switches the mock into th useReplyHandlers mode.
315 func (a *VppAdapter) MockReplyHandler(replyHandler ReplyHandler) {
316         repliesLock.Lock()
317         defer repliesLock.Unlock()
318
319         replyHandlers = append(replyHandlers, replyHandler)
320         mode = useReplyHandlers
321 }