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