Binary API 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         . "github.com/onsi/gomega"
22
23         "git.fd.io/govpp.git/adapter/mock"
24         "git.fd.io/govpp.git/api"
25         "git.fd.io/govpp.git/examples/binapi/interface_types"
26         "git.fd.io/govpp.git/examples/binapi/interfaces"
27         "git.fd.io/govpp.git/examples/binapi/memif"
28         "git.fd.io/govpp.git/examples/binapi/vpe"
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 TestRequestReplyMemifCreate(t *testing.T) {
60         ctx := setupTest(t)
61         defer ctx.teardownTest()
62
63         // mock reply
64         ctx.mockVpp.MockReply(&memif.MemifCreateReply{
65                 SwIfIndex: 4,
66         })
67
68         request := &memif.MemifCreate{
69                 Role:       10,
70                 ID:         12,
71                 RingSize:   8000,
72                 BufferSize: 50,
73         }
74         reply := &memif.MemifCreateReply{}
75
76         err := ctx.ch.SendRequest(request).ReceiveReply(reply)
77         Expect(err).ShouldNot(HaveOccurred())
78         Expect(reply.Retval).To(BeEquivalentTo(0),
79                 "Incorrect Retval value for MemifCreate")
80         Expect(reply.SwIfIndex).To(BeEquivalentTo(4),
81                 "Incorrect SwIfIndex value for MemifCreate")
82 }
83
84 func TestRequestReplyMemifDelete(t *testing.T) {
85         ctx := setupTest(t)
86         defer ctx.teardownTest()
87
88         // mock reply
89         ctx.mockVpp.MockReply(&memif.MemifDeleteReply{})
90
91         request := &memif.MemifDelete{
92                 SwIfIndex: 15,
93         }
94         reply := &memif.MemifDeleteReply{}
95
96         err := ctx.ch.SendRequest(request).ReceiveReply(reply)
97         Expect(err).ShouldNot(HaveOccurred())
98 }
99
100 func TestRequestReplyMemifDetails(t *testing.T) {
101         ctx := setupTest(t)
102         defer ctx.teardownTest()
103
104         // mock reply
105         ctx.mockVpp.MockReply(&memif.MemifDetails{
106                 SwIfIndex: 25,
107                 IfName:    "memif-name",
108                 Role:      0,
109         })
110
111         request := &memif.MemifDump{}
112         reply := &memif.MemifDetails{}
113
114         err := ctx.ch.SendRequest(request).ReceiveReply(reply)
115         Expect(err).ShouldNot(HaveOccurred())
116         Expect(reply.SwIfIndex).To(BeEquivalentTo(25),
117                 "Incorrect SwIfIndex value for MemifDetails")
118         Expect(reply.IfName).ToNot(BeEmpty(),
119                 "MemifDetails IfName is empty byte array")
120         Expect(reply.Role).To(BeEquivalentTo(0),
121                 "Incorrect Role value for MemifDetails")
122 }
123
124 func TestMultiRequestReplySwInterfaceMemifDump(t *testing.T) {
125         ctx := setupTest(t)
126         defer ctx.teardownTest()
127
128         // mock reply
129         var msgs []api.Message
130         for i := 1; i <= 10; i++ {
131                 msgs = append(msgs, &memif.MemifDetails{
132                         SwIfIndex: interface_types.InterfaceIndex(i),
133                 })
134         }
135         ctx.mockVpp.MockReply(msgs...)
136         ctx.mockVpp.MockReply(&ControlPingReply{})
137
138         reqCtx := ctx.ch.SendMultiRequest(&memif.MemifDump{})
139         cnt := 0
140         for {
141                 msg := &memif.MemifDetails{}
142                 stop, err := reqCtx.ReceiveReply(msg)
143                 if stop {
144                         break
145                 }
146                 Expect(err).ShouldNot(HaveOccurred())
147                 cnt++
148         }
149         Expect(cnt).To(BeEquivalentTo(10))
150 }
151
152 func TestNotificationEvent(t *testing.T) {
153         ctx := setupTest(t)
154         defer ctx.teardownTest()
155
156         // subscribe for notification
157         notifChan := make(chan api.Message, 1)
158         sub, err := ctx.ch.SubscribeNotification(notifChan, &interfaces.SwInterfaceEvent{})
159         Expect(err).ShouldNot(HaveOccurred())
160
161         // mock event and force its delivery
162         ctx.mockVpp.MockReply(&interfaces.SwInterfaceEvent{
163                 SwIfIndex: 2,
164                 Flags:     interface_types.IF_STATUS_API_FLAG_LINK_UP,
165         })
166         ctx.mockVpp.SendMsg(0, []byte(""))
167
168         // receive the notification
169         var notif *interfaces.SwInterfaceEvent
170         Eventually(func() *interfaces.SwInterfaceEvent {
171                 select {
172                 case n := <-notifChan:
173                         notif = n.(*interfaces.SwInterfaceEvent)
174                         return notif
175                 default:
176                         return nil
177                 }
178         }).ShouldNot(BeNil())
179
180         // verify the received notifications
181         Expect(notif.SwIfIndex).To(BeEquivalentTo(2), "Incorrect SwIfIndex value for SwInterfaceSetFlags")
182         Expect(notif.Flags).To(BeEquivalentTo(interface_types.IF_STATUS_API_FLAG_LINK_UP), "Incorrect LinkUpDown value for SwInterfaceSetFlags")
183
184         err = sub.Unsubscribe()
185         Expect(err).ShouldNot(HaveOccurred())
186 }
187
188 func TestSetReplyTimeout(t *testing.T) {
189         ctx := setupTest(t)
190         defer ctx.teardownTest()
191
192         ctx.ch.SetReplyTimeout(time.Millisecond)
193
194         // mock reply
195         ctx.mockVpp.MockReply(&ControlPingReply{})
196
197         // first one request should work
198         err := ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
199         Expect(err).ShouldNot(HaveOccurred())
200
201         // no other reply ready - expect timeout
202         err = ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
203         Expect(err).Should(HaveOccurred())
204         Expect(err.Error()).To(ContainSubstring("timeout"))
205 }
206
207 func TestSetReplyTimeoutMultiRequest(t *testing.T) {
208         ctx := setupTest(t)
209         defer ctx.teardownTest()
210
211         ctx.ch.SetReplyTimeout(time.Millisecond * 100)
212
213         // mock reply
214         ctx.mockVpp.MockReply(
215                 &interfaces.SwInterfaceDetails{
216                         SwIfIndex:     1,
217                         InterfaceName: "if-name-test",
218                 },
219                 &interfaces.SwInterfaceDetails{
220                         SwIfIndex:     2,
221                         InterfaceName: "if-name-test",
222                 },
223                 &interfaces.SwInterfaceDetails{
224                         SwIfIndex:     3,
225                         InterfaceName: "if-name-test",
226                 },
227         )
228         ctx.mockVpp.MockReply(&ControlPingReply{})
229
230         cnt := 0
231         sendMultiRequest := func() error {
232                 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
233                 for {
234                         msg := &interfaces.SwInterfaceDetails{}
235                         stop, err := reqCtx.ReceiveReply(msg)
236                         if err != nil {
237                                 return err
238                         }
239                         if stop {
240                                 break
241                         }
242                         cnt++
243                 }
244                 return nil
245         }
246
247         // first one request should work
248         err := sendMultiRequest()
249         Expect(err).ShouldNot(HaveOccurred())
250
251         // no other reply ready - expect timeout
252         err = sendMultiRequest()
253         Expect(err).Should(HaveOccurred())
254         Expect(err.Error()).To(ContainSubstring("timeout"))
255
256         Expect(cnt).To(BeEquivalentTo(3))
257 }
258
259 func TestReceiveReplyNegative(t *testing.T) {
260         ctx := setupTest(t)
261         defer ctx.teardownTest()
262
263         // invalid context 1
264         reqCtx1 := &requestCtx{}
265         err := reqCtx1.ReceiveReply(&ControlPingReply{})
266         Expect(err).Should(HaveOccurred())
267         Expect(err.Error()).To(ContainSubstring("invalid request context"))
268
269         // invalid context 2
270         reqCtx2 := &multiRequestCtx{}
271         _, err = reqCtx2.ReceiveReply(&ControlPingReply{})
272         Expect(err).Should(HaveOccurred())
273         Expect(err.Error()).To(ContainSubstring("invalid request context"))
274
275         // NU
276         reqCtx3 := &requestCtx{}
277         err = reqCtx3.ReceiveReply(nil)
278         Expect(err).Should(HaveOccurred())
279         Expect(err.Error()).To(ContainSubstring("invalid request context"))
280 }
281
282 func TestMultiRequestDouble(t *testing.T) {
283         ctx := setupTest(t)
284         defer ctx.teardownTest()
285
286         // mock reply
287         var msgs []mock.MsgWithContext
288         for i := 1; i <= 3; i++ {
289                 msgs = append(msgs, mock.MsgWithContext{
290                         Msg: &interfaces.SwInterfaceDetails{
291                                 SwIfIndex:     interface_types.InterfaceIndex(i),
292                                 InterfaceName: "if-name-test",
293                         },
294                         Multipart: true,
295                         SeqNum:    1,
296                 })
297         }
298         msgs = append(msgs, mock.MsgWithContext{Msg: &ControlPingReply{}, Multipart: true, SeqNum: 1})
299
300         for i := 1; i <= 3; i++ {
301                 msgs = append(msgs,
302                         mock.MsgWithContext{
303                                 Msg: &interfaces.SwInterfaceDetails{
304                                         SwIfIndex:     interface_types.InterfaceIndex(i),
305                                         InterfaceName: "if-name-test",
306                                 },
307                                 Multipart: true,
308                                 SeqNum:    2,
309                         })
310         }
311         msgs = append(msgs, mock.MsgWithContext{Msg: &ControlPingReply{}, Multipart: true, SeqNum: 2})
312
313         ctx.mockVpp.MockReplyWithContext(msgs...)
314
315         cnt := 0
316         var sendMultiRequest = func() error {
317                 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
318                 for {
319                         msg := &interfaces.SwInterfaceDetails{}
320                         stop, err := reqCtx.ReceiveReply(msg)
321                         if stop {
322                                 break
323                         }
324                         if err != nil {
325                                 return err
326                         }
327                         cnt++
328                 }
329                 return nil
330         }
331
332         err := sendMultiRequest()
333         Expect(err).ShouldNot(HaveOccurred())
334
335         err = sendMultiRequest()
336         Expect(err).ShouldNot(HaveOccurred())
337
338         Expect(cnt).To(BeEquivalentTo(6))
339 }
340
341 func TestReceiveReplyAfterTimeout(t *testing.T) {
342         ctx := setupTest(t)
343         defer ctx.teardownTest()
344
345         ctx.ch.SetReplyTimeout(time.Millisecond)
346
347         // mock reply
348         ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &ControlPingReply{}, SeqNum: 1})
349         // first one request should work
350
351         err := ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
352         Expect(err).ShouldNot(HaveOccurred())
353
354         err = ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
355         Expect(err).Should(HaveOccurred())
356         Expect(err.Error()).To(ContainSubstring("timeout"))
357
358         ctx.mockVpp.MockReplyWithContext(
359                 // simulating late reply
360                 mock.MsgWithContext{
361                         Msg:    &ControlPingReply{},
362                         SeqNum: 2,
363                 },
364                 // normal reply for next request
365                 mock.MsgWithContext{
366                         Msg:    &interfaces.SwInterfaceSetFlagsReply{},
367                         SeqNum: 3,
368                 },
369         )
370
371         req := &interfaces.SwInterfaceSetFlags{
372                 SwIfIndex: 1,
373                 Flags:     interface_types.IF_STATUS_API_FLAG_ADMIN_UP,
374         }
375         reply := &interfaces.SwInterfaceSetFlagsReply{}
376
377         // should succeed
378         err = ctx.ch.SendRequest(req).ReceiveReply(reply)
379         Expect(err).ShouldNot(HaveOccurred())
380 }
381
382 func TestReceiveReplyAfterTimeoutMultiRequest(t *testing.T) {
383         /*
384                 TODO: fix mock adapter
385                 This test will fail because mock adapter will stop sending replies
386                 when it encounters control_ping_reply from multi request,
387                 thus never sending reply for next request
388         */
389         t.Skip()
390
391         ctx := setupTest(t)
392         defer ctx.teardownTest()
393
394         ctx.ch.SetReplyTimeout(time.Millisecond * 100)
395
396         // mock reply
397         ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &ControlPingReply{}, SeqNum: 1})
398
399         // first one request should work
400         err := ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
401         Expect(err).ShouldNot(HaveOccurred())
402
403         cnt := 0
404         var sendMultiRequest = func() error {
405                 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
406                 for {
407                         msg := &interfaces.SwInterfaceDetails{}
408                         stop, err := reqCtx.ReceiveReply(msg)
409                         if stop {
410                                 break
411                         }
412                         if err != nil {
413                                 return err
414                         }
415                         cnt++
416                 }
417                 return nil
418         }
419
420         err = sendMultiRequest()
421         Expect(err).Should(HaveOccurred())
422         Expect(err.Error()).To(ContainSubstring("timeout"))
423         Expect(cnt).To(BeEquivalentTo(0))
424
425         // simulating late replies
426         var msgs []mock.MsgWithContext
427         for i := 1; i <= 3; i++ {
428                 msgs = append(msgs, mock.MsgWithContext{
429                         Msg: &interfaces.SwInterfaceDetails{
430                                 SwIfIndex:     interface_types.InterfaceIndex(i),
431                                 InterfaceName: "if-name-test",
432                         },
433                         Multipart: true,
434                         SeqNum:    2,
435                 })
436         }
437         msgs = append(msgs, mock.MsgWithContext{Msg: &ControlPingReply{}, Multipart: true, SeqNum: 2})
438         ctx.mockVpp.MockReplyWithContext(msgs...)
439
440         // normal reply for next request
441         ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &interfaces.SwInterfaceSetFlagsReply{}, SeqNum: 3})
442
443         req := &interfaces.SwInterfaceSetFlags{
444                 SwIfIndex: 1,
445                 Flags:     interface_types.IF_STATUS_API_FLAG_ADMIN_UP,
446         }
447         reply := &interfaces.SwInterfaceSetFlagsReply{}
448
449         // should succeed
450         err = ctx.ch.SendRequest(req).ReceiveReply(reply)
451         Expect(err).ShouldNot(HaveOccurred())
452 }
453
454 func TestInvalidMessageID(t *testing.T) {
455         ctx := setupTest(t)
456         defer ctx.teardownTest()
457
458         // mock reply
459         ctx.mockVpp.MockReply(&vpe.ShowVersionReply{})
460         ctx.mockVpp.MockReply(&vpe.ShowVersionReply{})
461
462         // first one request should work
463         err := ctx.ch.SendRequest(&vpe.ShowVersion{}).ReceiveReply(&vpe.ShowVersionReply{})
464         Expect(err).ShouldNot(HaveOccurred())
465
466         // second should fail with error invalid message ID
467         err = ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
468         Expect(err).Should(HaveOccurred())
469         Expect(err.Error()).To(ContainSubstring("unexpected message"))
470 }