mock adapter: Group all replies for one request under one call to MockReply
[govpp.git] / core / connection_test.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 core_test
16
17 import (
18         "testing"
19
20         "git.fd.io/govpp.git/adapter/mock"
21         "git.fd.io/govpp.git/api"
22         "git.fd.io/govpp.git/core"
23         "git.fd.io/govpp.git/core/bin_api/vpe"
24         "git.fd.io/govpp.git/examples/bin_api/interfaces"
25         "git.fd.io/govpp.git/examples/bin_api/stats"
26
27         "git.fd.io/govpp.git/codec"
28         . "github.com/onsi/gomega"
29 )
30
31 type testCtx struct {
32         mockVpp *mock.VppAdapter
33         conn    *core.Connection
34         ch      api.Channel
35 }
36
37 func setupTest(t *testing.T, bufferedChan bool) *testCtx {
38         RegisterTestingT(t)
39
40         ctx := &testCtx{}
41         ctx.mockVpp = &mock.VppAdapter{}
42
43         var err error
44         ctx.conn, err = core.Connect(ctx.mockVpp)
45         Expect(err).ShouldNot(HaveOccurred())
46
47         if bufferedChan {
48                 ctx.ch, err = ctx.conn.NewAPIChannelBuffered(100, 100)
49         } else {
50                 ctx.ch, err = ctx.conn.NewAPIChannel()
51         }
52         Expect(err).ShouldNot(HaveOccurred())
53
54         return ctx
55 }
56
57 func (ctx *testCtx) teardownTest() {
58         ctx.ch.Close()
59         ctx.conn.Disconnect()
60 }
61
62 func TestSimpleRequest(t *testing.T) {
63         ctx := setupTest(t, false)
64         defer ctx.teardownTest()
65
66         ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: -5})
67
68         req := &vpe.ControlPing{}
69         reply := &vpe.ControlPingReply{}
70
71         // send the request and receive a reply
72         ctx.ch.GetRequestChannel() <- &api.VppRequest{Message: req}
73         vppReply := <-ctx.ch.GetReplyChannel()
74
75         Expect(vppReply).ShouldNot(BeNil())
76         Expect(vppReply.Error).ShouldNot(HaveOccurred())
77
78         // decode the message
79         err := ctx.ch.GetMessageDecoder().DecodeMsg(vppReply.Data, reply)
80         Expect(err).ShouldNot(HaveOccurred())
81
82         Expect(reply.Retval).To(BeEquivalentTo(-5))
83 }
84
85 func TestMultiRequest(t *testing.T) {
86         ctx := setupTest(t, false)
87         defer ctx.teardownTest()
88
89         msgs := []api.Message{}
90         for m := 0; m < 10; m++ {
91                 msgs = append(msgs, &interfaces.SwInterfaceDetails{})
92         }
93         ctx.mockVpp.MockReply(msgs...)
94         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
95
96         // send multipart request
97         ctx.ch.GetRequestChannel() <- &api.VppRequest{Message: &interfaces.SwInterfaceDump{}, Multipart: true}
98
99         cnt := 0
100         for {
101                 // receive a reply
102                 vppReply := <-ctx.ch.GetReplyChannel()
103                 if vppReply.LastReplyReceived {
104                         break // break out of the loop
105                 }
106                 Expect(vppReply.Error).ShouldNot(HaveOccurred())
107
108                 // decode the message
109                 reply := &interfaces.SwInterfaceDetails{}
110                 err := ctx.ch.GetMessageDecoder().DecodeMsg(vppReply.Data, reply)
111                 Expect(err).ShouldNot(HaveOccurred())
112                 cnt++
113         }
114
115         Expect(cnt).To(BeEquivalentTo(10))
116 }
117
118 func TestNotifications(t *testing.T) {
119         ctx := setupTest(t, false)
120         defer ctx.teardownTest()
121
122         // subscribe for notification
123         notifChan := make(chan api.Message, 1)
124         subscription := &api.NotifSubscription{
125                 NotifChan:  notifChan,
126                 MsgFactory: interfaces.NewSwInterfaceSetFlags,
127         }
128         ctx.ch.GetNotificationChannel() <- &api.NotifSubscribeRequest{
129                 Subscription: subscription,
130                 Subscribe:    true,
131         }
132         err := <-ctx.ch.GetNotificationReplyChannel()
133         Expect(err).ShouldNot(HaveOccurred())
134
135         // mock the notification and force its delivery
136         ctx.mockVpp.MockReply(&interfaces.SwInterfaceSetFlags{
137                 SwIfIndex:   3,
138                 AdminUpDown: 1,
139         })
140         ctx.mockVpp.SendMsg(0, []byte{0})
141
142         // receive the notification
143         notif := (<-notifChan).(*interfaces.SwInterfaceSetFlags)
144
145         Expect(notif.SwIfIndex).To(BeEquivalentTo(3))
146
147         // unsubscribe notification
148         ctx.ch.GetNotificationChannel() <- &api.NotifSubscribeRequest{
149                 Subscription: subscription,
150                 Subscribe:    false,
151         }
152         err = <-ctx.ch.GetNotificationReplyChannel()
153         Expect(err).ShouldNot(HaveOccurred())
154 }
155
156 func TestNilConnection(t *testing.T) {
157         RegisterTestingT(t)
158         var conn *core.Connection
159
160         ch, err := conn.NewAPIChannel()
161         Expect(ch).Should(BeNil())
162         Expect(err).Should(HaveOccurred())
163         Expect(err.Error()).To(ContainSubstring("nil"))
164
165         ch, err = conn.NewAPIChannelBuffered(1, 1)
166         Expect(ch).Should(BeNil())
167         Expect(err).Should(HaveOccurred())
168         Expect(err.Error()).To(ContainSubstring("nil"))
169 }
170
171 func TestDoubleConnection(t *testing.T) {
172         ctx := setupTest(t, false)
173         defer ctx.teardownTest()
174
175         conn, err := core.Connect(ctx.mockVpp)
176         Expect(err).Should(HaveOccurred())
177         Expect(err.Error()).To(ContainSubstring("only one connection per process"))
178         Expect(conn).Should(BeNil())
179 }
180
181 func TestAsyncConnection(t *testing.T) {
182         ctx := setupTest(t, false)
183         defer ctx.teardownTest()
184
185         ctx.conn.Disconnect()
186         conn, ch, err := core.AsyncConnect(ctx.mockVpp)
187         ctx.conn = conn
188
189         Expect(err).ShouldNot(HaveOccurred())
190         Expect(conn).ShouldNot(BeNil())
191
192         ev := <-ch
193         Expect(ev.State).Should(BeEquivalentTo(core.Connected))
194 }
195
196 func TestFullBuffer(t *testing.T) {
197         ctx := setupTest(t, false)
198         defer ctx.teardownTest()
199
200         // close the default API channel
201         ctx.ch.Close()
202
203         // create a new channel with limited buffer sizes
204         var err error
205         ctx.ch, err = ctx.conn.NewAPIChannelBuffered(10, 1)
206         Expect(err).ShouldNot(HaveOccurred())
207
208         // send multiple requests, only one reply should be read
209         for i := 0; i < 20; i++ {
210                 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
211                 ctx.ch.GetRequestChannel() <- &api.VppRequest{Message: &vpe.ControlPing{}}
212         }
213
214         vppReply := <-ctx.ch.GetReplyChannel()
215         Expect(vppReply).ShouldNot(BeNil())
216
217         var received bool
218         select {
219         case <-ctx.ch.GetReplyChannel():
220                 received = true // this should not happen
221         default:
222                 received = false // no reply to be received
223         }
224         Expect(received).Should(BeFalse(), "A reply has been recieved, should had been ignored.")
225 }
226
227 func TestCodec(t *testing.T) {
228         RegisterTestingT(t)
229
230         msgCodec := &codec.MsgCodec{}
231
232         // request
233         data, err := msgCodec.EncodeMsg(&interfaces.CreateLoopback{MacAddress: []byte{1, 2, 3, 4, 5, 6}}, 11)
234         Expect(err).ShouldNot(HaveOccurred())
235         Expect(data).ShouldNot(BeEmpty())
236
237         msg1 := &interfaces.CreateLoopback{}
238         err = msgCodec.DecodeMsg(data, msg1)
239         Expect(err).ShouldNot(HaveOccurred())
240         Expect(msg1.MacAddress).To(BeEquivalentTo([]byte{1, 2, 3, 4, 5, 6}))
241
242         // reply
243         data, err = msgCodec.EncodeMsg(&vpe.ControlPingReply{Retval: 55}, 22)
244         Expect(err).ShouldNot(HaveOccurred())
245         Expect(data).ShouldNot(BeEmpty())
246
247         msg2 := &vpe.ControlPingReply{}
248         err = msgCodec.DecodeMsg(data, msg2)
249         Expect(err).ShouldNot(HaveOccurred())
250         Expect(msg2.Retval).To(BeEquivalentTo(55))
251
252         // other
253         data, err = msgCodec.EncodeMsg(&stats.VnetIP4FibCounters{VrfID: 77}, 33)
254         Expect(err).ShouldNot(HaveOccurred())
255         Expect(data).ShouldNot(BeEmpty())
256
257         msg3 := &stats.VnetIP4FibCounters{}
258         err = msgCodec.DecodeMsg(data, msg3)
259         Expect(err).ShouldNot(HaveOccurred())
260         Expect(msg3.VrfID).To(BeEquivalentTo(77))
261 }
262
263 func TestCodecNegative(t *testing.T) {
264         RegisterTestingT(t)
265
266         msgCodec := &codec.MsgCodec{}
267
268         // nil message for encoding
269         data, err := msgCodec.EncodeMsg(nil, 15)
270         Expect(err).Should(HaveOccurred())
271         Expect(err.Error()).To(ContainSubstring("nil message"))
272         Expect(data).Should(BeNil())
273
274         // nil message for decoding
275         err = msgCodec.DecodeMsg(data, nil)
276         Expect(err).Should(HaveOccurred())
277         Expect(err.Error()).To(ContainSubstring("nil message"))
278
279         // nil data for decoding
280         err = msgCodec.DecodeMsg(nil, &vpe.ControlPingReply{})
281         Expect(err).Should(HaveOccurred())
282         Expect(err.Error()).To(ContainSubstring("EOF"))
283 }
284
285 func TestSimpleRequestsWithSequenceNumbers(t *testing.T) {
286         ctx := setupTest(t, false)
287         defer ctx.teardownTest()
288
289         var reqCtx []api.RequestCtx
290         for i := 0; i < 10; i++ {
291                 ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: int32(i)})
292                 req := &vpe.ControlPing{}
293                 reqCtx = append(reqCtx, ctx.ch.SendRequest(req))
294         }
295
296         for i := 0; i < 10; i++ {
297                 reply := &vpe.ControlPingReply{}
298                 err := reqCtx[i].ReceiveReply(reply)
299                 Expect(err).ShouldNot(HaveOccurred())
300                 Expect(reply.Retval).To(BeEquivalentTo(i))
301         }
302 }
303
304 func TestMultiRequestsWithSequenceNumbers(t *testing.T) {
305         ctx := setupTest(t, false)
306         defer ctx.teardownTest()
307
308         msgs := []api.Message{}
309         for i := 0; i < 10; i++ {
310                 msgs = append(msgs, &interfaces.SwInterfaceDetails{SwIfIndex: uint32(i)})
311         }
312         ctx.mockVpp.MockReply(msgs...)
313         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
314
315         // send multipart request
316         reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
317
318         cnt := 0
319         for {
320                 Expect(cnt < 11).To(BeTrue())
321
322                 // receive a reply
323                 reply := &interfaces.SwInterfaceDetails{}
324                 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
325
326                 if lastReplyReceived {
327                         break // break out of the loop
328                 }
329
330                 Expect(err).ShouldNot(HaveOccurred())
331                 Expect(reply.SwIfIndex).To(BeEquivalentTo(cnt))
332
333                 cnt++
334         }
335
336         Expect(cnt).To(BeEquivalentTo(10))
337 }
338
339 func TestSimpleRequestWithTimeout(t *testing.T) {
340         ctx := setupTest(t, true)
341         defer ctx.teardownTest()
342
343         // reply for a previous timeouted requests to be ignored
344         ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
345                 Msg:    &vpe.ControlPingReply{Retval: 1},
346                 SeqNum: 0,
347         })
348
349         // send reply later
350         req1 := &vpe.ControlPing{}
351         reqCtx1 := ctx.ch.SendRequest(req1)
352
353         reply := &vpe.ControlPingReply{}
354         err := reqCtx1.ReceiveReply(reply)
355         Expect(err).ToNot(BeNil())
356         Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
357
358         ctx.mockVpp.MockReplyWithContext(
359                 // reply for the previous request
360                 mock.MsgWithContext{
361                         Msg:    &vpe.ControlPingReply{Retval: 1},
362                         SeqNum: 1,
363                 },
364                 // reply for the next request
365                 mock.MsgWithContext{
366                         Msg:    &vpe.ControlPingReply{Retval: 2},
367                         SeqNum: 2,
368                 })
369
370         // next request
371         req2 := &vpe.ControlPing{}
372         reqCtx2 := ctx.ch.SendRequest(req2)
373
374         // second request should ignore the first reply and return the second one
375         reply = &vpe.ControlPingReply{}
376         err = reqCtx2.ReceiveReply(reply)
377         Expect(err).To(BeNil())
378         Expect(reply.Retval).To(BeEquivalentTo(2))
379 }
380
381 func TestSimpleRequestsWithMissingReply(t *testing.T) {
382         ctx := setupTest(t, false)
383         defer ctx.teardownTest()
384
385         // request without reply
386         req1 := &vpe.ControlPing{}
387         reqCtx1 := ctx.ch.SendRequest(req1)
388
389         // another request without reply
390         req2 := &vpe.ControlPing{}
391         reqCtx2 := ctx.ch.SendRequest(req2)
392
393         // third request with reply
394         ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
395                 Msg:    &vpe.ControlPingReply{Retval: 3},
396                 SeqNum: 3,
397         })
398         req3 := &vpe.ControlPing{}
399         reqCtx3 := ctx.ch.SendRequest(req3)
400
401         // the first two should fail, but not consume reply for the 3rd
402         reply := &vpe.ControlPingReply{}
403         err := reqCtx1.ReceiveReply(reply)
404         Expect(err).ToNot(BeNil())
405         Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
406
407         reply = &vpe.ControlPingReply{}
408         err = reqCtx2.ReceiveReply(reply)
409         Expect(err).ToNot(BeNil())
410         Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 2"))
411
412         // the second request should succeed
413         reply = &vpe.ControlPingReply{}
414         err = reqCtx3.ReceiveReply(reply)
415         Expect(err).To(BeNil())
416         Expect(reply.Retval).To(BeEquivalentTo(3))
417 }
418
419 func TestMultiRequestsWithErrors(t *testing.T) {
420         ctx := setupTest(t, false)
421         defer ctx.teardownTest()
422
423         // replies for a previous timeouted requests to be ignored
424         msgs := []mock.MsgWithContext{}
425         msgs = append(msgs,
426                 mock.MsgWithContext{
427                         Msg:    &vpe.ControlPingReply{Retval: 1},
428                         SeqNum: 0xffff - 1,
429                 },
430                 mock.MsgWithContext{
431                         Msg:    &vpe.ControlPingReply{Retval: 1},
432                         SeqNum: 0xffff,
433                 },
434                 mock.MsgWithContext{
435                         Msg:    &vpe.ControlPingReply{Retval: 1},
436                         SeqNum: 0,
437                 })
438
439         for i := 0; i < 10; i++ {
440                 msgs = append(msgs,
441                         mock.MsgWithContext{
442                                 Msg:       &interfaces.SwInterfaceDetails{SwIfIndex: uint32(i)},
443                                 SeqNum:    1,
444                                 Multipart: true,
445                         })
446         }
447         // missing finalizing control ping
448
449         // reply for a next request
450         msgs = append(msgs,
451                 mock.MsgWithContext{
452                         Msg:       &vpe.ControlPingReply{Retval: 2},
453                         SeqNum:    2,
454                         Multipart: false,
455                 })
456
457         // queue replies
458         ctx.mockVpp.MockReplyWithContext(msgs...)
459
460         // send multipart request
461         reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
462
463         for i := 0; i < 10; i++ {
464                 // receive multi-part replies
465                 reply := &interfaces.SwInterfaceDetails{}
466                 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
467
468                 Expect(lastReplyReceived).To(BeFalse())
469                 Expect(err).ShouldNot(HaveOccurred())
470                 Expect(reply.SwIfIndex).To(BeEquivalentTo(i))
471         }
472
473         // missing closing control ping
474         reply := &interfaces.SwInterfaceDetails{}
475         _, err := reqCtx.ReceiveReply(reply)
476         Expect(err).ToNot(BeNil())
477         Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
478
479         // try again - still fails and nothing consumed
480         _, err = reqCtx.ReceiveReply(reply)
481         Expect(err).ToNot(BeNil())
482         Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
483
484         // reply for the second request has not been consumed
485         reqCtx2 := ctx.ch.SendRequest(&vpe.ControlPing{})
486         reply2 := &vpe.ControlPingReply{}
487         err = reqCtx2.ReceiveReply(reply2)
488         Expect(err).To(BeNil())
489         Expect(reply2.Retval).To(BeEquivalentTo(2))
490 }
491
492 func TestRequestsOrdering(t *testing.T) {
493         ctx := setupTest(t, false)
494         defer ctx.teardownTest()
495
496         // the orderings of SendRequest and ReceiveReply calls should match, otherwise
497         // some replies will get thrown away
498
499         // first request
500         ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: 1})
501         req1 := &vpe.ControlPing{}
502         reqCtx1 := ctx.ch.SendRequest(req1)
503
504         // second request
505         ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: 2})
506         req2 := &vpe.ControlPing{}
507         reqCtx2 := ctx.ch.SendRequest(req2)
508
509         // if reply for the second request is read first, the reply for the first
510         // request gets thrown away.
511         reply2 := &vpe.ControlPingReply{}
512         err := reqCtx2.ReceiveReply(reply2)
513         Expect(err).To(BeNil())
514         Expect(reply2.Retval).To(BeEquivalentTo(2))
515
516         // first request has already been considered closed
517         reply1 := &vpe.ControlPingReply{}
518         err = reqCtx1.ReceiveReply(reply1)
519         Expect(err).ToNot(BeNil())
520         Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
521 }
522
523 func TestCycleOverSetOfSequenceNumbers(t *testing.T) {
524         ctx := setupTest(t, true)
525         defer ctx.teardownTest()
526
527         numIters := 0xffff + 100
528         reqCtx := make(map[int]api.RequestCtx)
529
530         for i := 0; i < numIters+30; /* receiver is 30 reqs behind */ i++ {
531                 if i < numIters {
532                         ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: int32(i)})
533                         req := &vpe.ControlPing{}
534                         reqCtx[i] = ctx.ch.SendRequest(req)
535                 }
536                 if i > 30 {
537                         reply := &vpe.ControlPingReply{}
538                         err := reqCtx[i-30].ReceiveReply(reply)
539                         Expect(err).ShouldNot(HaveOccurred())
540                         Expect(reply.Retval).To(BeEquivalentTo(i - 30))
541                 }
542         }
543 }