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