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