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