Refactor GoVPP
[govpp.git] / core / channel_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
16
17 import (
18         "testing"
19         "time"
20
21         "git.fd.io/govpp.git/adapter/mock"
22         "git.fd.io/govpp.git/examples/bin_api/interfaces"
23         "git.fd.io/govpp.git/examples/bin_api/memif"
24         "git.fd.io/govpp.git/examples/bin_api/tap"
25
26         "git.fd.io/govpp.git/api"
27         . "github.com/onsi/gomega"
28 )
29
30 type testCtx struct {
31         mockVpp *mock.VppAdapter
32         conn    *Connection
33         ch      api.Channel
34 }
35
36 func setupTest(t *testing.T) *testCtx {
37         RegisterTestingT(t)
38
39         ctx := &testCtx{
40                 mockVpp: &mock.VppAdapter{},
41         }
42
43         var err error
44         ctx.conn, err = Connect(ctx.mockVpp)
45         Expect(err).ShouldNot(HaveOccurred())
46
47         ctx.ch, err = ctx.conn.NewAPIChannel()
48         Expect(err).ShouldNot(HaveOccurred())
49
50         return ctx
51 }
52
53 func (ctx *testCtx) teardownTest() {
54         ctx.ch.Close()
55         ctx.conn.Disconnect()
56 }
57
58 func TestRequestReplyTapConnect(t *testing.T) {
59         ctx := setupTest(t)
60         defer ctx.teardownTest()
61
62         ctx.mockVpp.MockReply(&tap.TapConnectReply{
63                 Retval:    0,
64                 SwIfIndex: 1,
65         })
66         request := &tap.TapConnect{
67                 TapName:      []byte("test-tap-name"),
68                 UseRandomMac: 1,
69         }
70         reply := &tap.TapConnectReply{}
71
72         err := ctx.ch.SendRequest(request).ReceiveReply(reply)
73         Expect(err).ShouldNot(HaveOccurred())
74         Expect(reply.Retval).To(BeEquivalentTo(0), "Incorrect retval value for TapConnectReply")
75         Expect(reply.SwIfIndex).To(BeEquivalentTo(1), "Incorrect SwIfIndex value for TapConnectReply")
76 }
77
78 func TestRequestReplyTapModify(t *testing.T) {
79         ctx := setupTest(t)
80         defer ctx.teardownTest()
81
82         ctx.mockVpp.MockReply(&tap.TapModifyReply{
83                 SwIfIndex: 2,
84         })
85         request := &tap.TapModify{
86                 TapName:           []byte("test-tap-modify"),
87                 UseRandomMac:      1,
88                 CustomDevInstance: 1,
89         }
90         reply := &tap.TapModifyReply{}
91
92         err := ctx.ch.SendRequest(request).ReceiveReply(reply)
93         Expect(err).ShouldNot(HaveOccurred())
94         Expect(reply.Retval).To(BeEquivalentTo(0), "Incorrect retval value for TapModifyReply")
95         Expect(reply.SwIfIndex).To(BeEquivalentTo(2), "Incorrect SwIfIndex value for TapModifyReply")
96 }
97
98 func TestRequestReplyTapDelete(t *testing.T) {
99         ctx := setupTest(t)
100         defer ctx.teardownTest()
101
102         ctx.mockVpp.MockReply(&tap.TapDeleteReply{})
103         request := &tap.TapDelete{
104                 SwIfIndex: 3,
105         }
106         reply := &tap.TapDeleteReply{}
107
108         err := ctx.ch.SendRequest(request).ReceiveReply(reply)
109         Expect(err).ShouldNot(HaveOccurred())
110         Expect(reply.Retval).To(BeEquivalentTo(0), "Incorrect retval value for TapDeleteReply")
111 }
112
113 func TestRequestReplySwInterfaceTapDump(t *testing.T) {
114         ctx := setupTest(t)
115         defer ctx.teardownTest()
116
117         byteName := []byte("dev-name-test")
118         ctx.mockVpp.MockReply(&tap.SwInterfaceTapDetails{
119                 SwIfIndex: 25,
120                 DevName:   byteName,
121         })
122         request := &tap.SwInterfaceTapDump{}
123         reply := &tap.SwInterfaceTapDetails{}
124
125         err := ctx.ch.SendRequest(request).ReceiveReply(reply)
126         Expect(err).ShouldNot(HaveOccurred())
127         Expect(reply.SwIfIndex).To(BeEquivalentTo(25), "Incorrect SwIfIndex value for SwInterfaceTapDetails")
128         Expect(reply.DevName).ToNot(BeNil(), "Incorrect DevName value for SwInterfaceTapDetails")
129 }
130
131 func TestRequestReplyMemifCreate(t *testing.T) {
132         ctx := setupTest(t)
133         defer ctx.teardownTest()
134
135         ctx.mockVpp.MockReply(&memif.MemifCreateReply{
136                 SwIfIndex: 4,
137         })
138         request := &memif.MemifCreate{
139                 Role:       10,
140                 ID:         12,
141                 RingSize:   8000,
142                 BufferSize: 50,
143         }
144         reply := &memif.MemifCreateReply{}
145
146         err := ctx.ch.SendRequest(request).ReceiveReply(reply)
147         Expect(err).ShouldNot(HaveOccurred())
148         Expect(reply.Retval).To(BeEquivalentTo(0), "Incorrect Retval value for MemifCreate")
149         Expect(reply.SwIfIndex).To(BeEquivalentTo(4), "Incorrect SwIfIndex value for MemifCreate")
150 }
151
152 func TestRequestReplyMemifDelete(t *testing.T) {
153         ctx := setupTest(t)
154         defer ctx.teardownTest()
155
156         ctx.mockVpp.MockReply(&memif.MemifDeleteReply{})
157         request := &memif.MemifDelete{
158                 SwIfIndex: 15,
159         }
160         reply := &memif.MemifDeleteReply{}
161
162         err := ctx.ch.SendRequest(request).ReceiveReply(reply)
163         Expect(err).ShouldNot(HaveOccurred())
164         Expect(reply.Retval).To(BeEquivalentTo(0), "Incorrect Retval value for MemifDelete")
165 }
166
167 func TestRequestReplyMemifDetails(t *testing.T) {
168         ctx := setupTest(t)
169         defer ctx.teardownTest()
170
171         ctx.mockVpp.MockReply(&memif.MemifDetails{
172                 SwIfIndex: 25,
173                 IfName:    []byte("memif-name"),
174                 Role:      0,
175         })
176         request := &memif.MemifDump{}
177         reply := &memif.MemifDetails{}
178
179         err := ctx.ch.SendRequest(request).ReceiveReply(reply)
180         Expect(err).ShouldNot(HaveOccurred())
181         Expect(reply.SwIfIndex).To(BeEquivalentTo(25), "Incorrect SwIfIndex value for MemifDetails")
182         Expect(reply.IfName).ToNot(BeEmpty(), "MemifDetails IfName is empty byte array")
183         Expect(reply.Role).To(BeEquivalentTo(0), "Incorrect Role value for MemifDetails")
184 }
185
186 func TestMultiRequestReplySwInterfaceTapDump(t *testing.T) {
187         ctx := setupTest(t)
188         defer ctx.teardownTest()
189
190         // mock reply
191         var msgs []api.Message
192         for i := 1; i <= 10; i++ {
193                 msgs = append(msgs, &tap.SwInterfaceTapDetails{
194                         SwIfIndex: uint32(i),
195                         DevName:   []byte("dev-name-test"),
196                 })
197         }
198         ctx.mockVpp.MockReply(msgs...)
199         ctx.mockVpp.MockReply(&ControlPingReply{})
200
201         reqCtx := ctx.ch.SendMultiRequest(&tap.SwInterfaceTapDump{})
202         cnt := 0
203         for {
204                 msg := &tap.SwInterfaceTapDetails{}
205                 stop, err := reqCtx.ReceiveReply(msg)
206                 if stop {
207                         break // break out of the loop
208                 }
209                 Expect(err).ShouldNot(HaveOccurred())
210                 cnt++
211         }
212         Expect(cnt).To(BeEquivalentTo(10))
213 }
214
215 func TestMultiRequestReplySwInterfaceMemifDump(t *testing.T) {
216         ctx := setupTest(t)
217         defer ctx.teardownTest()
218
219         // mock reply
220         var msgs []api.Message
221         for i := 1; i <= 10; i++ {
222                 msgs = append(msgs, &memif.MemifDetails{
223                         SwIfIndex: uint32(i),
224                 })
225         }
226         ctx.mockVpp.MockReply(msgs...)
227         ctx.mockVpp.MockReply(&ControlPingReply{})
228
229         reqCtx := ctx.ch.SendMultiRequest(&memif.MemifDump{})
230         cnt := 0
231         for {
232                 msg := &memif.MemifDetails{}
233                 stop, err := reqCtx.ReceiveReply(msg)
234                 if stop {
235                         break // break out of the loop
236                 }
237                 Expect(err).ShouldNot(HaveOccurred())
238                 cnt++
239         }
240         Expect(cnt).To(BeEquivalentTo(10))
241 }
242
243 func TestNotifications(t *testing.T) {
244         ctx := setupTest(t)
245         defer ctx.teardownTest()
246
247         // subscribe for notification
248         notifChan := make(chan api.Message, 1)
249         subs, err := ctx.ch.SubscribeNotification(notifChan, interfaces.NewSwInterfaceSetFlags)
250         Expect(err).ShouldNot(HaveOccurred())
251
252         // mock the notification and force its delivery
253         ctx.mockVpp.MockReply(&interfaces.SwInterfaceSetFlags{
254                 SwIfIndex:   3,
255                 AdminUpDown: 1,
256         })
257         ctx.mockVpp.SendMsg(0, []byte(""))
258
259         // receive the notification
260         var notif *interfaces.SwInterfaceSetFlags
261         Eventually(func() *interfaces.SwInterfaceSetFlags {
262                 select {
263                 case n := <-notifChan:
264                         notif = n.(*interfaces.SwInterfaceSetFlags)
265                         return notif
266                 default:
267                         return nil
268                 }
269         }).ShouldNot(BeNil())
270
271         // verify the received notifications
272         Expect(notif.SwIfIndex).To(BeEquivalentTo(3), "Incorrect SwIfIndex value for SwInterfaceSetFlags")
273         Expect(notif.AdminUpDown).To(BeEquivalentTo(1), "Incorrect AdminUpDown value for SwInterfaceSetFlags")
274
275         ctx.ch.UnsubscribeNotification(subs)
276 }
277
278 func TestNotificationEvent(t *testing.T) {
279         ctx := setupTest(t)
280         defer ctx.teardownTest()
281
282         // subscribe for notification
283         notifChan := make(chan api.Message, 1)
284         subs, err := ctx.ch.SubscribeNotification(notifChan, interfaces.NewSwInterfaceEvent)
285         Expect(err).ShouldNot(HaveOccurred())
286
287         // mock the notification and force its delivery
288         ctx.mockVpp.MockReply(&interfaces.SwInterfaceEvent{
289                 SwIfIndex:  2,
290                 LinkUpDown: 1,
291         })
292         ctx.mockVpp.SendMsg(0, []byte(""))
293
294         // receive the notification
295         var notif *interfaces.SwInterfaceEvent
296         Eventually(func() *interfaces.SwInterfaceEvent {
297                 select {
298                 case n := <-notifChan:
299                         notif = n.(*interfaces.SwInterfaceEvent)
300                         return notif
301                 default:
302                         return nil
303                 }
304         }).ShouldNot(BeNil())
305
306         // verify the received notifications
307         Expect(notif.SwIfIndex).To(BeEquivalentTo(2), "Incorrect SwIfIndex value for SwInterfaceSetFlags")
308         Expect(notif.LinkUpDown).To(BeEquivalentTo(1), "Incorrect LinkUpDown value for SwInterfaceSetFlags")
309
310         ctx.ch.UnsubscribeNotification(subs)
311 }
312
313 /*func TestCheckMessageCompatibility(t *testing.T) {
314         ctx := setupTest(t)
315         defer ctx.teardownTest()
316
317         err := ctx.ch.CheckMessageCompatibility(&interfaces.SwInterfaceSetFlags{})
318         Expect(err).ShouldNot(HaveOccurred())
319 }*/
320
321 func TestSetReplyTimeout(t *testing.T) {
322         ctx := setupTest(t)
323         defer ctx.teardownTest()
324
325         ctx.ch.SetReplyTimeout(time.Millisecond)
326
327         // first one request should work
328         ctx.mockVpp.MockReply(&ControlPingReply{})
329         err := ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
330         Expect(err).ShouldNot(HaveOccurred())
331
332         // no other reply ready - expect timeout
333         err = ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
334         Expect(err).Should(HaveOccurred())
335         Expect(err.Error()).To(ContainSubstring("timeout"))
336 }
337
338 func TestSetReplyTimeoutMultiRequest(t *testing.T) {
339         ctx := setupTest(t)
340         defer ctx.teardownTest()
341
342         ctx.ch.SetReplyTimeout(time.Millisecond)
343
344         var msgs []api.Message
345         for i := 1; i <= 3; i++ {
346                 msgs = append(msgs, &interfaces.SwInterfaceDetails{
347                         SwIfIndex:     uint32(i),
348                         InterfaceName: []byte("if-name-test"),
349                 })
350         }
351         ctx.mockVpp.MockReply(msgs...)
352         ctx.mockVpp.MockReply(&ControlPingReply{})
353
354         cnt := 0
355         sendMultiRequest := func() error {
356                 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
357                 for {
358                         msg := &interfaces.SwInterfaceDetails{}
359                         stop, err := reqCtx.ReceiveReply(msg)
360                         if stop {
361                                 break // break out of the loop
362                         }
363                         if err != nil {
364                                 return err
365                         }
366                         cnt++
367                 }
368                 return nil
369         }
370
371         // first one request should work
372         err := sendMultiRequest()
373         Expect(err).ShouldNot(HaveOccurred())
374
375         // no other reply ready - expect timeout
376         err = sendMultiRequest()
377         Expect(err).Should(HaveOccurred())
378         Expect(err.Error()).To(ContainSubstring("timeout"))
379
380         Expect(cnt).To(BeEquivalentTo(3))
381 }
382
383 func TestReceiveReplyNegative(t *testing.T) {
384         ctx := setupTest(t)
385         defer ctx.teardownTest()
386
387         // invalid context 1
388         reqCtx1 := &requestCtx{}
389         err := reqCtx1.ReceiveReply(&ControlPingReply{})
390         Expect(err).Should(HaveOccurred())
391         Expect(err.Error()).To(ContainSubstring("invalid request context"))
392
393         // invalid context 2
394         reqCtx2 := &multiRequestCtx{}
395         _, err = reqCtx2.ReceiveReply(&ControlPingReply{})
396         Expect(err).Should(HaveOccurred())
397         Expect(err.Error()).To(ContainSubstring("invalid request context"))
398
399         // NU
400         reqCtx3 := &requestCtx{}
401         err = reqCtx3.ReceiveReply(nil)
402         Expect(err).Should(HaveOccurred())
403         Expect(err.Error()).To(ContainSubstring("invalid request context"))
404 }
405
406 func TestMultiRequestDouble(t *testing.T) {
407         ctx := setupTest(t)
408         defer ctx.teardownTest()
409
410         // mock reply
411         var msgs []mock.MsgWithContext
412         for i := 1; i <= 3; i++ {
413                 msgs = append(msgs, mock.MsgWithContext{
414                         Msg: &interfaces.SwInterfaceDetails{
415                                 SwIfIndex:     uint32(i),
416                                 InterfaceName: []byte("if-name-test"),
417                         },
418                         Multipart: true,
419                         SeqNum:    1,
420                 })
421         }
422         msgs = append(msgs, mock.MsgWithContext{Msg: &ControlPingReply{}, Multipart: true, SeqNum: 1})
423
424         for i := 1; i <= 3; i++ {
425                 msgs = append(msgs,
426                         mock.MsgWithContext{
427                                 Msg: &interfaces.SwInterfaceDetails{
428                                         SwIfIndex:     uint32(i),
429                                         InterfaceName: []byte("if-name-test"),
430                                 },
431                                 Multipart: true,
432                                 SeqNum:    2,
433                         })
434         }
435         msgs = append(msgs, mock.MsgWithContext{Msg: &ControlPingReply{}, Multipart: true, SeqNum: 2})
436
437         ctx.mockVpp.MockReplyWithContext(msgs...)
438
439         cnt := 0
440         var sendMultiRequest = func() error {
441                 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
442                 for {
443                         msg := &interfaces.SwInterfaceDetails{}
444                         stop, err := reqCtx.ReceiveReply(msg)
445                         if stop {
446                                 break // break out of the loop
447                         }
448                         if err != nil {
449                                 return err
450                         }
451                         cnt++
452                 }
453                 return nil
454         }
455
456         err := sendMultiRequest()
457         Expect(err).ShouldNot(HaveOccurred())
458
459         err = sendMultiRequest()
460         Expect(err).ShouldNot(HaveOccurred())
461
462         Expect(cnt).To(BeEquivalentTo(6))
463 }
464
465 func TestReceiveReplyAfterTimeout(t *testing.T) {
466         ctx := setupTest(t)
467         defer ctx.teardownTest()
468
469         ctx.ch.SetReplyTimeout(time.Millisecond)
470
471         // first one request should work
472         ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &ControlPingReply{}, SeqNum: 1})
473         err := ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
474         Expect(err).ShouldNot(HaveOccurred())
475
476         err = ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
477         Expect(err).Should(HaveOccurred())
478         Expect(err.Error()).To(ContainSubstring("timeout"))
479
480         ctx.mockVpp.MockReplyWithContext(
481                 // simulating late reply
482                 mock.MsgWithContext{Msg: &ControlPingReply{}, SeqNum: 2},
483                 // normal reply for next request
484                 mock.MsgWithContext{Msg: &tap.TapConnectReply{}, SeqNum: 3})
485
486         req := &tap.TapConnect{
487                 TapName:      []byte("test-tap-name"),
488                 UseRandomMac: 1,
489         }
490         reply := &tap.TapConnectReply{}
491
492         // should succeed
493         err = ctx.ch.SendRequest(req).ReceiveReply(reply)
494         Expect(err).ShouldNot(HaveOccurred())
495 }
496
497 func TestReceiveReplyAfterTimeoutMultiRequest(t *testing.T) {
498         /*
499                 TODO: fix mock adapter
500                 This test will fail because mock adapter will stop sending replies
501                 when it encounters control_ping_reply from multi request,
502                 thus never sending reply for next request
503         */
504         t.Skip()
505
506         ctx := setupTest(t)
507         defer ctx.teardownTest()
508
509         ctx.ch.SetReplyTimeout(time.Millisecond * 100)
510
511         // first one request should work
512         ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &ControlPingReply{}, SeqNum: 1})
513         err := ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
514         Expect(err).ShouldNot(HaveOccurred())
515
516         cnt := 0
517         var sendMultiRequest = func() error {
518                 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
519                 for {
520                         msg := &interfaces.SwInterfaceDetails{}
521                         stop, err := reqCtx.ReceiveReply(msg)
522                         if stop {
523                                 break // break out of the loop
524                         }
525                         if err != nil {
526                                 return err
527                         }
528                         cnt++
529                 }
530                 return nil
531         }
532
533         err = sendMultiRequest()
534         Expect(err).Should(HaveOccurred())
535         Expect(err.Error()).To(ContainSubstring("timeout"))
536         Expect(cnt).To(BeEquivalentTo(0))
537
538         // simulating late replies
539         var msgs []mock.MsgWithContext
540         for i := 1; i <= 3; i++ {
541                 msgs = append(msgs, mock.MsgWithContext{
542                         Msg: &interfaces.SwInterfaceDetails{
543                                 SwIfIndex:     uint32(i),
544                                 InterfaceName: []byte("if-name-test"),
545                         },
546                         Multipart: true,
547                         SeqNum:    2,
548                 })
549         }
550         msgs = append(msgs, mock.MsgWithContext{Msg: &ControlPingReply{}, Multipart: true, SeqNum: 2})
551         ctx.mockVpp.MockReplyWithContext(msgs...)
552
553         // normal reply for next request
554         ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &tap.TapConnectReply{}, SeqNum: 3})
555
556         req := &tap.TapConnect{
557                 TapName:      []byte("test-tap-name"),
558                 UseRandomMac: 1,
559         }
560         reply := &tap.TapConnectReply{}
561
562         // should succeed
563         err = ctx.ch.SendRequest(req).ReceiveReply(reply)
564         Expect(err).ShouldNot(HaveOccurred())
565 }
566
567 /*func TestInvalidMessageID(t *testing.T) {
568         ctx := setupTest(t)
569         defer ctx.teardownTest()
570
571         // first one request should work
572         ctx.mockVpp.MockReply(&vpe.ShowVersionReply{})
573         err := ctx.ch.SendRequest(&vpe.ShowVersion{}).ReceiveReply(&vpe.ShowVersionReply{})
574         Expect(err).ShouldNot(HaveOccurred())
575
576         // second should fail with error invalid message ID
577         ctx.mockVpp.MockReply(&vpe.ShowVersionReply{})
578         err = ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
579         Expect(err).Should(HaveOccurred())
580         Expect(err.Error()).To(ContainSubstring("invalid message ID"))
581 }*/