Support mocking reply for more multi requests at once
[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         ctx.ch.SetReplyTimeout(time.Millisecond)
53
54         return ctx
55 }
56
57 func (ctx *testCtx) teardownTest() {
58         ctx.ch.Close()
59         ctx.conn.Disconnect()
60 }
61
62 func TestRequestReplyTapConnect(t *testing.T) {
63         ctx := setupTest(t)
64         defer ctx.teardownTest()
65
66         ctx.mockVpp.MockReply(&tap.TapConnectReply{
67                 Retval:    10,
68                 SwIfIndex: 1,
69         })
70         request := &tap.TapConnect{
71                 TapName:      []byte("test-tap-name"),
72                 UseRandomMac: 1,
73         }
74         reply := &tap.TapConnectReply{}
75
76         err := ctx.ch.SendRequest(request).ReceiveReply(reply)
77         Expect(err).ShouldNot(HaveOccurred())
78         Expect(reply.Retval).To(BeEquivalentTo(10), "Incorrect retval value for TapConnectReply")
79         Expect(reply.SwIfIndex).To(BeEquivalentTo(1), "Incorrect SwIfIndex value for TapConnectReply")
80 }
81
82 func TestRequestReplyTapModify(t *testing.T) {
83         ctx := setupTest(t)
84         defer ctx.teardownTest()
85
86         ctx.mockVpp.MockReply(&tap.TapModifyReply{
87                 Retval:    15,
88                 SwIfIndex: 2,
89         })
90         request := &tap.TapModify{
91                 TapName:           []byte("test-tap-modify"),
92                 UseRandomMac:      1,
93                 CustomDevInstance: 1,
94         }
95         reply := &tap.TapModifyReply{}
96
97         err := ctx.ch.SendRequest(request).ReceiveReply(reply)
98         Expect(err).ShouldNot(HaveOccurred())
99         Expect(reply.Retval).To(BeEquivalentTo(15), "Incorrect retval value for TapModifyReply")
100         Expect(reply.SwIfIndex).To(BeEquivalentTo(2), "Incorrect SwIfIndex value for TapModifyReply")
101 }
102
103 func TestRequestReplyTapDelete(t *testing.T) {
104         ctx := setupTest(t)
105         defer ctx.teardownTest()
106
107         ctx.mockVpp.MockReply(&tap.TapDeleteReply{
108                 Retval: 20,
109         })
110         request := &tap.TapDelete{
111                 SwIfIndex: 3,
112         }
113         reply := &tap.TapDeleteReply{}
114
115         err := ctx.ch.SendRequest(request).ReceiveReply(reply)
116         Expect(err).ShouldNot(HaveOccurred())
117         Expect(reply.Retval).To(BeEquivalentTo(20), "Incorrect retval value for TapDeleteReply")
118 }
119
120 func TestRequestReplySwInterfaceTapDump(t *testing.T) {
121         ctx := setupTest(t)
122         defer ctx.teardownTest()
123
124         byteName := []byte("dev-name-test")
125         ctx.mockVpp.MockReply(&tap.SwInterfaceTapDetails{
126                 SwIfIndex: 25,
127                 DevName:   byteName,
128         })
129         request := &tap.SwInterfaceTapDump{}
130         reply := &tap.SwInterfaceTapDetails{}
131
132         err := ctx.ch.SendRequest(request).ReceiveReply(reply)
133         Expect(err).ShouldNot(HaveOccurred())
134         Expect(reply.SwIfIndex).To(BeEquivalentTo(25), "Incorrect SwIfIndex value for SwInterfaceTapDetails")
135         Expect(reply.DevName).ToNot(BeNil(), "Incorrect DevName value for SwInterfaceTapDetails")
136 }
137
138 func TestRequestReplyMemifCreate(t *testing.T) {
139         ctx := setupTest(t)
140         defer ctx.teardownTest()
141
142         ctx.mockVpp.MockReply(&memif.MemifCreateReply{
143                 Retval:    22,
144                 SwIfIndex: 4,
145         })
146         request := &memif.MemifCreate{
147                 Role:       10,
148                 ID:         12,
149                 RingSize:   8000,
150                 BufferSize: 50,
151         }
152         reply := &memif.MemifCreateReply{}
153
154         err := ctx.ch.SendRequest(request).ReceiveReply(reply)
155         Expect(err).ShouldNot(HaveOccurred())
156         Expect(reply.Retval).To(BeEquivalentTo(22), "Incorrect Retval value for MemifCreate")
157         Expect(reply.SwIfIndex).To(BeEquivalentTo(4), "Incorrect SwIfIndex value for MemifCreate")
158 }
159
160 func TestRequestReplyMemifDelete(t *testing.T) {
161         ctx := setupTest(t)
162         defer ctx.teardownTest()
163
164         ctx.mockVpp.MockReply(&memif.MemifDeleteReply{
165                 Retval: 24,
166         })
167         request := &memif.MemifDelete{
168                 SwIfIndex: 15,
169         }
170         reply := &memif.MemifDeleteReply{}
171
172         err := ctx.ch.SendRequest(request).ReceiveReply(reply)
173         Expect(err).ShouldNot(HaveOccurred())
174         Expect(reply.Retval).To(BeEquivalentTo(24), "Incorrect Retval value for MemifDelete")
175 }
176
177 func TestRequestReplyMemifDetails(t *testing.T) {
178         ctx := setupTest(t)
179         defer ctx.teardownTest()
180
181         ctx.mockVpp.MockReply(&memif.MemifDetails{
182                 SwIfIndex: 25,
183                 IfName:    []byte("memif-name"),
184                 Role:      0,
185         })
186         request := &memif.MemifDump{}
187         reply := &memif.MemifDetails{}
188
189         err := ctx.ch.SendRequest(request).ReceiveReply(reply)
190         Expect(err).ShouldNot(HaveOccurred())
191         Expect(reply.SwIfIndex).To(BeEquivalentTo(25), "Incorrect SwIfIndex value for MemifDetails")
192         Expect(reply.IfName).ToNot(BeEmpty(), "MemifDetails IfName is empty byte array")
193         Expect(reply.Role).To(BeEquivalentTo(0), "Incorrect Role value for MemifDetails")
194 }
195
196 func TestMultiRequestReplySwInterfaceTapDump(t *testing.T) {
197         ctx := setupTest(t)
198         defer ctx.teardownTest()
199
200         // mock reply
201         for i := 1; i <= 10; i++ {
202                 ctx.mockVpp.MockReply(&tap.SwInterfaceTapDetails{
203                         SwIfIndex: uint32(i),
204                         DevName:   []byte("dev-name-test"),
205                 })
206         }
207         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
208
209         reqCtx := ctx.ch.SendMultiRequest(&tap.SwInterfaceTapDump{})
210         cnt := 0
211         for {
212                 msg := &tap.SwInterfaceTapDetails{}
213                 stop, err := reqCtx.ReceiveReply(msg)
214                 if stop {
215                         break // break out of the loop
216                 }
217                 Expect(err).ShouldNot(HaveOccurred())
218                 cnt++
219         }
220         Expect(cnt).To(BeEquivalentTo(10))
221 }
222
223 func TestMultiRequestReplySwInterfaceMemifDump(t *testing.T) {
224         ctx := setupTest(t)
225         defer ctx.teardownTest()
226
227         // mock reply
228         for i := 1; i <= 10; i++ {
229                 ctx.mockVpp.MockReply(&memif.MemifDetails{
230                         SwIfIndex: uint32(i),
231                 })
232         }
233         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
234
235         reqCtx := ctx.ch.SendMultiRequest(&memif.MemifDump{})
236         cnt := 0
237         for {
238                 msg := &memif.MemifDetails{}
239                 stop, err := reqCtx.ReceiveReply(msg)
240                 if stop {
241                         break // break out of the loop
242                 }
243                 Expect(err).ShouldNot(HaveOccurred())
244                 cnt++
245         }
246         Expect(cnt).To(BeEquivalentTo(10))
247 }
248
249 func TestNotifications(t *testing.T) {
250         ctx := setupTest(t)
251         defer ctx.teardownTest()
252
253         // subscribe for notification
254         notifChan := make(chan api.Message, 1)
255         subs, err := ctx.ch.SubscribeNotification(notifChan, interfaces.NewSwInterfaceSetFlags)
256         Expect(err).ShouldNot(HaveOccurred())
257
258         // mock the notification and force its delivery
259         ctx.mockVpp.MockReply(&interfaces.SwInterfaceSetFlags{
260                 SwIfIndex:   3,
261                 AdminUpDown: 1,
262         })
263         ctx.mockVpp.SendMsg(0, []byte(""))
264
265         // receive the notification
266         var notif *interfaces.SwInterfaceSetFlags
267         Eventually(func() *interfaces.SwInterfaceSetFlags {
268                 select {
269                 case n := <-notifChan:
270                         notif = n.(*interfaces.SwInterfaceSetFlags)
271                         return notif
272                 default:
273                         return nil
274                 }
275         }).ShouldNot(BeNil())
276
277         // verify the received notifications
278         Expect(notif.SwIfIndex).To(BeEquivalentTo(3), "Incorrect SwIfIndex value for SwInterfaceSetFlags")
279         Expect(notif.AdminUpDown).To(BeEquivalentTo(1), "Incorrect AdminUpDown value for SwInterfaceSetFlags")
280
281         ctx.ch.UnsubscribeNotification(subs)
282 }
283
284 func TestNotificationEvent(t *testing.T) {
285         ctx := setupTest(t)
286         defer ctx.teardownTest()
287
288         // subscribe for notification
289         notifChan := make(chan api.Message, 1)
290         subs, err := ctx.ch.SubscribeNotification(notifChan, interfaces.NewSwInterfaceEvent)
291         Expect(err).ShouldNot(HaveOccurred())
292
293         // mock the notification and force its delivery
294         ctx.mockVpp.MockReply(&interfaces.SwInterfaceEvent{
295                 SwIfIndex:  2,
296                 LinkUpDown: 1,
297         })
298         ctx.mockVpp.SendMsg(0, []byte(""))
299
300         // receive the notification
301         var notif *interfaces.SwInterfaceEvent
302         Eventually(func() *interfaces.SwInterfaceEvent {
303                 select {
304                 case n := <-notifChan:
305                         notif = n.(*interfaces.SwInterfaceEvent)
306                         return notif
307                 default:
308                         return nil
309                 }
310         }).ShouldNot(BeNil())
311
312         // verify the received notifications
313         Expect(notif.SwIfIndex).To(BeEquivalentTo(2), "Incorrect SwIfIndex value for SwInterfaceSetFlags")
314         Expect(notif.LinkUpDown).To(BeEquivalentTo(1), "Incorrect LinkUpDown value for SwInterfaceSetFlags")
315
316         ctx.ch.UnsubscribeNotification(subs)
317 }
318
319 func TestCheckMessageCompatibility(t *testing.T) {
320         ctx := setupTest(t)
321         defer ctx.teardownTest()
322
323         err := ctx.ch.CheckMessageCompatibility(&interfaces.SwInterfaceSetFlags{})
324         Expect(err).ShouldNot(HaveOccurred())
325 }
326
327 func TestSetReplyTimeout(t *testing.T) {
328         ctx := setupTest(t)
329         defer ctx.teardownTest()
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         for i := 1; i <= 3; i++ {
347                 ctx.mockVpp.MockReply(&interfaces.SwInterfaceDetails{
348                         SwIfIndex:     uint32(i),
349                         InterfaceName: []byte("if-name-test"),
350                 })
351         }
352         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
353
354         cnt := 0
355         sendMultiRequest := func() error {
356                 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
357                 for {
358                         msg := &interfaces.SwInterfaceDetails{}
359                         stop, err := reqCtx.ReceiveReply(msg)
360                         if stop {
361                                 break // break out of the loop
362                         }
363                         if err != nil {
364                                 return err
365                         }
366                         cnt++
367                 }
368                 return nil
369         }
370
371         // first one request should work
372         err := sendMultiRequest()
373         Expect(err).ShouldNot(HaveOccurred())
374
375         // no other reply ready - expect timeout
376         err = sendMultiRequest()
377         Expect(err).Should(HaveOccurred())
378         Expect(err.Error()).To(ContainSubstring("timeout"))
379
380         Expect(cnt).To(BeEquivalentTo(3))
381 }
382
383 func TestReceiveReplyNegative(t *testing.T) {
384         ctx := setupTest(t)
385         defer ctx.teardownTest()
386
387         // invalid context 1
388         reqCtx1 := &api.RequestCtx{}
389         err := reqCtx1.ReceiveReply(&vpe.ControlPingReply{})
390         Expect(err).Should(HaveOccurred())
391         Expect(err.Error()).To(ContainSubstring("invalid request context"))
392
393         // invalid context 2
394         reqCtx2 := &api.MultiRequestCtx{}
395         _, err = reqCtx2.ReceiveReply(&vpe.ControlPingReply{})
396         Expect(err).Should(HaveOccurred())
397         Expect(err.Error()).To(ContainSubstring("invalid request context"))
398
399         // NU
400         reqCtx3 := &api.RequestCtx{}
401         err = reqCtx3.ReceiveReply(nil)
402         Expect(err).Should(HaveOccurred())
403         Expect(err.Error()).To(ContainSubstring("invalid request context"))
404 }
405
406 func TestMultiRequestDouble(t *testing.T) {
407         ctx := setupTest(t)
408         defer ctx.teardownTest()
409
410         // mock reply
411         for i := 1; i <= 3; i++ {
412                 ctx.mockVpp.MockReply(&interfaces.SwInterfaceDetails{
413                         SwIfIndex:     uint32(i),
414                         InterfaceName: []byte("if-name-test"),
415                 })
416         }
417         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
418         for i := 1; i <= 3; i++ {
419                 ctx.mockVpp.MockReply(&interfaces.SwInterfaceDetails{
420                         SwIfIndex:     uint32(i),
421                         InterfaceName: []byte("if-name-test"),
422                 })
423         }
424         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
425
426         cnt := 0
427         sendMultiRequest := func() error {
428                 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
429                 for {
430                         msg := &interfaces.SwInterfaceDetails{}
431                         stop, err := reqCtx.ReceiveReply(msg)
432                         if stop {
433                                 break // break out of the loop
434                         }
435                         if err != nil {
436                                 return err
437                         }
438                         cnt++
439                 }
440                 return nil
441         }
442
443         err := sendMultiRequest()
444         Expect(err).ShouldNot(HaveOccurred())
445
446         err = sendMultiRequest()
447         Expect(err).ShouldNot(HaveOccurred())
448
449         Expect(cnt).To(BeEquivalentTo(6))
450 }