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