Ignore invalid message ID if last request timed out
[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
325 func TestSetReplyTimeout(t *testing.T) {
326         ctx := setupTest(t)
327         defer ctx.teardownTest()
328
329         ctx.ch.SetReplyTimeout(time.Millisecond)
330
331         // first one request should work
332         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
333         err := ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
334         Expect(err).ShouldNot(HaveOccurred())
335
336         // no other reply ready - expect timeout
337         err = ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
338         Expect(err).Should(HaveOccurred())
339         Expect(err.Error()).To(ContainSubstring("timeout"))
340 }
341
342 func TestSetReplyTimeoutMultiRequest(t *testing.T) {
343         ctx := setupTest(t)
344         defer ctx.teardownTest()
345
346         ctx.ch.SetReplyTimeout(time.Millisecond)
347
348         for i := 1; i <= 3; i++ {
349                 ctx.mockVpp.MockReply(&interfaces.SwInterfaceDetails{
350                         SwIfIndex:     uint32(i),
351                         InterfaceName: []byte("if-name-test"),
352                 })
353         }
354         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
355
356         cnt := 0
357         sendMultiRequest := func() error {
358                 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
359                 for {
360                         msg := &interfaces.SwInterfaceDetails{}
361                         stop, err := reqCtx.ReceiveReply(msg)
362                         if stop {
363                                 break // break out of the loop
364                         }
365                         if err != nil {
366                                 return err
367                         }
368                         cnt++
369                 }
370                 return nil
371         }
372
373         // first one request should work
374         err := sendMultiRequest()
375         Expect(err).ShouldNot(HaveOccurred())
376
377         // no other reply ready - expect timeout
378         err = sendMultiRequest()
379         Expect(err).Should(HaveOccurred())
380         Expect(err.Error()).To(ContainSubstring("timeout"))
381
382         Expect(cnt).To(BeEquivalentTo(3))
383 }
384
385 func TestReceiveReplyNegative(t *testing.T) {
386         ctx := setupTest(t)
387         defer ctx.teardownTest()
388
389         // invalid context 1
390         reqCtx1 := &api.RequestCtx{}
391         err := reqCtx1.ReceiveReply(&vpe.ControlPingReply{})
392         Expect(err).Should(HaveOccurred())
393         Expect(err.Error()).To(ContainSubstring("invalid request context"))
394
395         // invalid context 2
396         reqCtx2 := &api.MultiRequestCtx{}
397         _, err = reqCtx2.ReceiveReply(&vpe.ControlPingReply{})
398         Expect(err).Should(HaveOccurred())
399         Expect(err.Error()).To(ContainSubstring("invalid request context"))
400
401         // NU
402         reqCtx3 := &api.RequestCtx{}
403         err = reqCtx3.ReceiveReply(nil)
404         Expect(err).Should(HaveOccurred())
405         Expect(err.Error()).To(ContainSubstring("invalid request context"))
406 }
407
408 func TestMultiRequestDouble(t *testing.T) {
409         ctx := setupTest(t)
410         defer ctx.teardownTest()
411
412         // mock reply
413         for i := 1; i <= 3; i++ {
414                 ctx.mockVpp.MockReply(&interfaces.SwInterfaceDetails{
415                         SwIfIndex:     uint32(i),
416                         InterfaceName: []byte("if-name-test"),
417                 })
418         }
419         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
420         for i := 1; i <= 3; i++ {
421                 ctx.mockVpp.MockReply(&interfaces.SwInterfaceDetails{
422                         SwIfIndex:     uint32(i),
423                         InterfaceName: []byte("if-name-test"),
424                 })
425         }
426         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
427
428         cnt := 0
429         var sendMultiRequest = func() error {
430                 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
431                 for {
432                         msg := &interfaces.SwInterfaceDetails{}
433                         stop, err := reqCtx.ReceiveReply(msg)
434                         if stop {
435                                 break // break out of the loop
436                         }
437                         if err != nil {
438                                 return err
439                         }
440                         cnt++
441                 }
442                 return nil
443         }
444
445         err := sendMultiRequest()
446         Expect(err).ShouldNot(HaveOccurred())
447
448         err = sendMultiRequest()
449         Expect(err).ShouldNot(HaveOccurred())
450
451         Expect(cnt).To(BeEquivalentTo(6))
452 }
453
454 func TestReceiveReplyAfterTimeout(t *testing.T) {
455         ctx := setupTest(t)
456         defer ctx.teardownTest()
457
458         ctx.ch.SetReplyTimeout(time.Millisecond)
459
460         // first one request should work
461         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
462         err := ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
463         Expect(err).ShouldNot(HaveOccurred())
464
465         err = ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
466         Expect(err).Should(HaveOccurred())
467         Expect(err.Error()).To(ContainSubstring("timeout"))
468
469         // simulating late reply
470         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
471
472         // normal reply for next request
473         ctx.mockVpp.MockReply(&tap.TapConnectReply{})
474
475         req := &tap.TapConnect{
476                 TapName:      []byte("test-tap-name"),
477                 UseRandomMac: 1,
478         }
479         reply := &tap.TapConnectReply{}
480
481         // should succeed
482         err = ctx.ch.SendRequest(req).ReceiveReply(reply)
483         Expect(err).ShouldNot(HaveOccurred())
484 }
485
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 func TestReceiveReplyAfterTimeoutMultiRequest(t *testing.T) {
493         ctx := setupTest(t)
494         defer ctx.teardownTest()
495
496         ctx.ch.SetReplyTimeout(time.Millisecond * 100)
497
498         // first one request should work
499         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
500         err := ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
501         Expect(err).ShouldNot(HaveOccurred())
502
503         cnt := 0
504         var sendMultiRequest = func() error {
505                 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
506                 for {
507                         msg := &interfaces.SwInterfaceDetails{}
508                         stop, err := reqCtx.ReceiveReply(msg)
509                         if stop {
510                                 break // break out of the loop
511                         }
512                         if err != nil {
513                                 return err
514                         }
515                         cnt++
516                 }
517                 return nil
518         }
519
520         err = sendMultiRequest()
521         Expect(err).Should(HaveOccurred())
522         Expect(err.Error()).To(ContainSubstring("timeout"))
523         Expect(cnt).To(BeEquivalentTo(0))
524
525         // simulating late replies
526         for i := 1; i <= 3; i++ {
527                 ctx.mockVpp.MockReply(&interfaces.SwInterfaceDetails{
528                         SwIfIndex:     uint32(i),
529                         InterfaceName: []byte("if-name-test"),
530                 })
531         }
532         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
533
534         // normal reply for next request
535         ctx.mockVpp.MockReply(&tap.TapConnectReply{})
536
537         req := &tap.TapConnect{
538                 TapName:      []byte("test-tap-name"),
539                 UseRandomMac: 1,
540         }
541         reply := &tap.TapConnectReply{}
542
543         // should succeed
544         err = ctx.ch.SendRequest(req).ReceiveReply(reply)
545         Expect(err).ShouldNot(HaveOccurred())
546 }
547 */