Pair requests with replies using sequence numbers
[govpp.git] / core / core_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_test
16
17 import (
18         "testing"
19
20         "git.fd.io/govpp.git/adapter/mock"
21         "git.fd.io/govpp.git/api"
22         "git.fd.io/govpp.git/core"
23         "git.fd.io/govpp.git/core/bin_api/vpe"
24         "git.fd.io/govpp.git/examples/bin_api/interfaces"
25         "git.fd.io/govpp.git/examples/bin_api/stats"
26
27         . "github.com/onsi/gomega"
28 )
29
30 type testCtx struct {
31         mockVpp *mock.VppAdapter
32         conn    *core.Connection
33         ch      *api.Channel
34 }
35
36 func setupTest(t *testing.T, bufferedChan bool) *testCtx {
37         RegisterTestingT(t)
38
39         ctx := &testCtx{}
40         ctx.mockVpp = &mock.VppAdapter{}
41
42         var err error
43         ctx.conn, err = core.Connect(ctx.mockVpp)
44         Expect(err).ShouldNot(HaveOccurred())
45
46         if bufferedChan {
47                 ctx.ch, err = ctx.conn.NewAPIChannelBuffered(100, 100)
48         } else {
49                 ctx.ch, err = ctx.conn.NewAPIChannel()
50         }
51         Expect(err).ShouldNot(HaveOccurred())
52
53         return ctx
54 }
55
56 func (ctx *testCtx) teardownTest() {
57         ctx.ch.Close()
58         ctx.conn.Disconnect()
59 }
60
61 func TestSimpleRequest(t *testing.T) {
62         ctx := setupTest(t, false)
63         defer ctx.teardownTest()
64
65         ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: -5}, false)
66
67         req := &vpe.ControlPing{}
68         reply := &vpe.ControlPingReply{}
69
70         // send the request and receive a reply
71         ctx.ch.ReqChan <- &api.VppRequest{Message: req}
72         vppReply := <-ctx.ch.ReplyChan
73
74         Expect(vppReply).ShouldNot(BeNil())
75         Expect(vppReply.Error).ShouldNot(HaveOccurred())
76
77         // decode the message
78         err := ctx.ch.MsgDecoder.DecodeMsg(vppReply.Data, reply)
79         Expect(err).ShouldNot(HaveOccurred())
80
81         Expect(reply.Retval).To(BeEquivalentTo(-5))
82 }
83
84 func TestMultiRequest(t *testing.T) {
85         ctx := setupTest(t, false)
86         defer ctx.teardownTest()
87
88         for m := 0; m < 10; m++ {
89                 ctx.mockVpp.MockReply(&interfaces.SwInterfaceDetails{}, true)
90         }
91         ctx.mockVpp.MockReply(&vpe.ControlPingReply{}, true)
92
93         // send multipart request
94         ctx.ch.ReqChan <- &api.VppRequest{Message: &interfaces.SwInterfaceDump{}, Multipart: true}
95
96         cnt := 0
97         for {
98                 // receive a reply
99                 vppReply := <-ctx.ch.ReplyChan
100                 if vppReply.LastReplyReceived {
101                         break // break out of the loop
102                 }
103                 Expect(vppReply.Error).ShouldNot(HaveOccurred())
104
105                 // decode the message
106                 reply := &interfaces.SwInterfaceDetails{}
107                 err := ctx.ch.MsgDecoder.DecodeMsg(vppReply.Data, reply)
108                 Expect(err).ShouldNot(HaveOccurred())
109                 cnt++
110         }
111
112         Expect(cnt).To(BeEquivalentTo(10))
113 }
114
115 func TestNotifications(t *testing.T) {
116         ctx := setupTest(t, false)
117         defer ctx.teardownTest()
118
119         // subscribe for notification
120         notifChan := make(chan api.Message, 1)
121         subscription := &api.NotifSubscription{
122                 NotifChan:  notifChan,
123                 MsgFactory: interfaces.NewSwInterfaceSetFlags,
124         }
125         ctx.ch.NotifSubsChan <- &api.NotifSubscribeRequest{
126                 Subscription: subscription,
127                 Subscribe:    true,
128         }
129         err := <-ctx.ch.NotifSubsReplyChan
130         Expect(err).ShouldNot(HaveOccurred())
131
132         // mock the notification and force its delivery
133         ctx.mockVpp.MockReply(&interfaces.SwInterfaceSetFlags{
134                 SwIfIndex:   3,
135                 AdminUpDown: 1,
136         }, false)
137         ctx.mockVpp.SendMsg(0, []byte{0})
138
139         // receive the notification
140         notif := (<-notifChan).(*interfaces.SwInterfaceSetFlags)
141
142         Expect(notif.SwIfIndex).To(BeEquivalentTo(3))
143
144         // unsubscribe notification
145         ctx.ch.NotifSubsChan <- &api.NotifSubscribeRequest{
146                 Subscription: subscription,
147                 Subscribe:    false,
148         }
149         err = <-ctx.ch.NotifSubsReplyChan
150         Expect(err).ShouldNot(HaveOccurred())
151 }
152
153 func TestNilConnection(t *testing.T) {
154         RegisterTestingT(t)
155         var conn *core.Connection
156
157         ch, err := conn.NewAPIChannel()
158         Expect(ch).Should(BeNil())
159         Expect(err).Should(HaveOccurred())
160         Expect(err.Error()).To(ContainSubstring("nil"))
161
162         ch, err = conn.NewAPIChannelBuffered(1, 1)
163         Expect(ch).Should(BeNil())
164         Expect(err).Should(HaveOccurred())
165         Expect(err.Error()).To(ContainSubstring("nil"))
166 }
167
168 func TestDoubleConnection(t *testing.T) {
169         ctx := setupTest(t, false)
170         defer ctx.teardownTest()
171
172         conn, err := core.Connect(ctx.mockVpp)
173         Expect(err).Should(HaveOccurred())
174         Expect(err.Error()).To(ContainSubstring("only one connection per process"))
175         Expect(conn).Should(BeNil())
176 }
177
178 func TestAsyncConnection(t *testing.T) {
179         ctx := setupTest(t, false)
180         defer ctx.teardownTest()
181
182         ctx.conn.Disconnect()
183         conn, ch, err := core.AsyncConnect(ctx.mockVpp)
184         ctx.conn = conn
185
186         Expect(err).ShouldNot(HaveOccurred())
187         Expect(conn).ShouldNot(BeNil())
188
189         ev := <-ch
190         Expect(ev.State).Should(BeEquivalentTo(core.Connected))
191 }
192
193 func TestFullBuffer(t *testing.T) {
194         ctx := setupTest(t, false)
195         defer ctx.teardownTest()
196
197         // close the default API channel
198         ctx.ch.Close()
199
200         // create a new channel with limited buffer sizes
201         var err error
202         ctx.ch, err = ctx.conn.NewAPIChannelBuffered(10, 1)
203         Expect(err).ShouldNot(HaveOccurred())
204
205         // send multiple requests, only one reply should be read
206         for i := 0; i < 20; i++ {
207                 ctx.mockVpp.MockReply(&vpe.ControlPingReply{}, false)
208                 ctx.ch.ReqChan <- &api.VppRequest{Message: &vpe.ControlPing{}}
209         }
210
211         vppReply := <-ctx.ch.ReplyChan
212         Expect(vppReply).ShouldNot(BeNil())
213
214         var received bool
215         select {
216         case <-ctx.ch.ReplyChan:
217                 received = true // this should not happen
218         default:
219                 received = false // no reply to be received
220         }
221         Expect(received).Should(BeFalse(), "A reply has been recieved, should had been ignored.")
222 }
223
224 func TestCodec(t *testing.T) {
225         RegisterTestingT(t)
226
227         codec := &core.MsgCodec{}
228
229         // request
230         data, err := codec.EncodeMsg(&interfaces.CreateLoopback{MacAddress: []byte{1, 2, 3, 4, 5, 6}}, 11)
231         Expect(err).ShouldNot(HaveOccurred())
232         Expect(data).ShouldNot(BeEmpty())
233
234         msg1 := &interfaces.CreateLoopback{}
235         err = codec.DecodeMsg(data, msg1)
236         Expect(err).ShouldNot(HaveOccurred())
237         Expect(msg1.MacAddress).To(BeEquivalentTo([]byte{1, 2, 3, 4, 5, 6}))
238
239         // reply
240         data, err = codec.EncodeMsg(&vpe.ControlPingReply{Retval: 55}, 22)
241         Expect(err).ShouldNot(HaveOccurred())
242         Expect(data).ShouldNot(BeEmpty())
243
244         msg2 := &vpe.ControlPingReply{}
245         err = codec.DecodeMsg(data, msg2)
246         Expect(err).ShouldNot(HaveOccurred())
247         Expect(msg2.Retval).To(BeEquivalentTo(55))
248
249         // other
250         data, err = codec.EncodeMsg(&stats.VnetIP4FibCounters{VrfID: 77}, 33)
251         Expect(err).ShouldNot(HaveOccurred())
252         Expect(data).ShouldNot(BeEmpty())
253
254         msg3 := &stats.VnetIP4FibCounters{}
255         err = codec.DecodeMsg(data, msg3)
256         Expect(err).ShouldNot(HaveOccurred())
257         Expect(msg3.VrfID).To(BeEquivalentTo(77))
258 }
259
260 func TestCodecNegative(t *testing.T) {
261         RegisterTestingT(t)
262
263         codec := &core.MsgCodec{}
264
265         // nil message for encoding
266         data, err := codec.EncodeMsg(nil, 15)
267         Expect(err).Should(HaveOccurred())
268         Expect(err.Error()).To(ContainSubstring("nil message"))
269         Expect(data).Should(BeNil())
270
271         // nil message for decoding
272         err = codec.DecodeMsg(data, nil)
273         Expect(err).Should(HaveOccurred())
274         Expect(err.Error()).To(ContainSubstring("nil message"))
275
276         // nil data for decoding
277         err = codec.DecodeMsg(nil, &vpe.ControlPingReply{})
278         Expect(err).Should(HaveOccurred())
279         Expect(err.Error()).To(ContainSubstring("EOF"))
280 }
281
282 func TestSimpleRequestsWithSequenceNumbers(t *testing.T) {
283         ctx := setupTest(t, false)
284         defer ctx.teardownTest()
285
286         var reqCtx []*api.RequestCtx
287         for i := 0; i < 10; i++ {
288                 ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: int32(i)}, false)
289                 req := &vpe.ControlPing{}
290                 reqCtx = append(reqCtx, ctx.ch.SendRequest(req))
291         }
292
293         for i := 0; i < 10; i++ {
294                 reply := &vpe.ControlPingReply{}
295                 err := reqCtx[i].ReceiveReply(reply)
296                 Expect(err).ShouldNot(HaveOccurred())
297                 Expect(reply.Retval).To(BeEquivalentTo(i))
298         }
299 }
300
301 func TestMultiRequestsWithSequenceNumbers(t *testing.T) {
302         ctx := setupTest(t, false)
303         defer ctx.teardownTest()
304
305         for i := 0; i < 10; i++ {
306                 ctx.mockVpp.MockReply(&interfaces.SwInterfaceDetails{SwIfIndex: uint32(i)}, true)
307         }
308         ctx.mockVpp.MockReply(&vpe.ControlPingReply{}, true)
309
310         // send multipart request
311         reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
312
313         cnt := 0
314         for {
315                 Expect(cnt < 11).To(BeTrue())
316
317                 // receive a reply
318                 reply := &interfaces.SwInterfaceDetails{}
319                 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
320
321                 if lastReplyReceived {
322                         break // break out of the loop
323                 }
324
325                 Expect(err).ShouldNot(HaveOccurred())
326                 Expect(reply.SwIfIndex).To(BeEquivalentTo(cnt))
327
328                 cnt++
329         }
330
331         Expect(cnt).To(BeEquivalentTo(10))
332 }
333
334 func TestSimpleRequestWithTimeout(t *testing.T) {
335         ctx := setupTest(t, true)
336         defer ctx.teardownTest()
337
338         // reply for a previous timeouted requests to be ignored
339         ctx.mockVpp.MockReplyWithSeqNum(&vpe.ControlPingReply{Retval: 1}, false,0)
340
341         // send reply later
342         req1 := &vpe.ControlPing{}
343         reqCtx1 := ctx.ch.SendRequest(req1)
344
345         reply := &vpe.ControlPingReply{}
346         err := reqCtx1.ReceiveReply(reply)
347         Expect(err).ToNot(BeNil())
348         Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
349
350         // reply for the previous request
351         ctx.mockVpp.MockReplyWithSeqNum(&vpe.ControlPingReply{Retval: 1}, false,1)
352
353         // next request
354         ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: 2}, false)
355         req2 := &vpe.ControlPing{}
356         reqCtx2 := ctx.ch.SendRequest(req2)
357
358         // second request should ignore the first reply and return the second one
359         reply = &vpe.ControlPingReply{}
360         err = reqCtx2.ReceiveReply(reply)
361         Expect(err).To(BeNil())
362         Expect(reply.Retval).To(BeEquivalentTo(2))
363 }
364
365 func TestSimpleRequestsWithMissingReply(t *testing.T) {
366         ctx := setupTest(t, false)
367         defer ctx.teardownTest()
368
369         // request without reply
370         req1 := &vpe.ControlPing{}
371         reqCtx1 := ctx.ch.SendRequest(req1)
372
373         // another request without reply
374         req2 := &vpe.ControlPing{}
375         reqCtx2 := ctx.ch.SendRequest(req2)
376
377         // third request with reply
378         ctx.mockVpp.MockReplyWithSeqNum(&vpe.ControlPingReply{Retval: 3}, false, 3)
379         req3 := &vpe.ControlPing{}
380         reqCtx3 := ctx.ch.SendRequest(req3)
381
382         // the first two should fail, but not consume reply for the 3rd
383         reply := &vpe.ControlPingReply{}
384         err := reqCtx1.ReceiveReply(reply)
385         Expect(err).ToNot(BeNil())
386         Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
387
388         reply = &vpe.ControlPingReply{}
389         err = reqCtx2.ReceiveReply(reply)
390         Expect(err).ToNot(BeNil())
391         Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 2"))
392
393         // the second request should succeed
394         reply = &vpe.ControlPingReply{}
395         err = reqCtx3.ReceiveReply(reply)
396         Expect(err).To(BeNil())
397         Expect(reply.Retval).To(BeEquivalentTo(3))
398 }
399
400 func TestMultiRequestsWithErrors(t *testing.T) {
401         ctx := setupTest(t, false)
402         defer ctx.teardownTest()
403
404         // reply for a previous timeouted requests to be ignored
405         ctx.mockVpp.MockReplyWithSeqNum(&vpe.ControlPingReply{Retval: 1}, false,0xffff - 1)
406         ctx.mockVpp.MockReplyWithSeqNum(&vpe.ControlPingReply{Retval: 1}, false,0xffff)
407         ctx.mockVpp.MockReplyWithSeqNum(&vpe.ControlPingReply{Retval: 1}, false,0)
408
409         for i := 0; i < 10; i++ {
410                 ctx.mockVpp.MockReply(&interfaces.SwInterfaceDetails{SwIfIndex: uint32(i)}, true)
411         }
412         // missing finalizing control ping
413
414         // reply for a next request
415         ctx.mockVpp.MockReplyWithSeqNum(&vpe.ControlPingReply{Retval: 2}, false,2)
416
417         // send multipart request
418         reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
419
420         for i := 0; i < 10; i++ {
421                 // receive multi-part replies
422                 reply := &interfaces.SwInterfaceDetails{}
423                 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
424
425                 Expect(lastReplyReceived).To(BeFalse())
426                 Expect(err).ShouldNot(HaveOccurred())
427                 Expect(reply.SwIfIndex).To(BeEquivalentTo(i))
428         }
429
430         // missing closing control ping
431         reply := &interfaces.SwInterfaceDetails{}
432         _, err := reqCtx.ReceiveReply(reply)
433         Expect(err).ToNot(BeNil())
434         Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
435
436         // try again - still fails and nothing consumed
437         _, err = reqCtx.ReceiveReply(reply)
438         Expect(err).ToNot(BeNil())
439         Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
440
441         // reply for the second request has not been consumed
442         reqCtx2 := ctx.ch.SendRequest(&vpe.ControlPing{})
443         reply2 := &vpe.ControlPingReply{}
444         err = reqCtx2.ReceiveReply(reply2)
445         Expect(err).To(BeNil())
446         Expect(reply2.Retval).To(BeEquivalentTo(2))
447 }
448
449 func TestRequestsOrdering(t *testing.T) {
450         ctx := setupTest(t, false)
451         defer ctx.teardownTest()
452
453         // the orderings of SendRequest and ReceiveReply calls should match, otherwise
454         // some replies will get thrown away
455
456         // first request
457         ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: 1}, false)
458         req1 := &vpe.ControlPing{}
459         reqCtx1 := ctx.ch.SendRequest(req1)
460
461         // second request
462         ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: 2}, false)
463         req2 := &vpe.ControlPing{}
464         reqCtx2 := ctx.ch.SendRequest(req2)
465
466         // if reply for the second request is read first, the reply for the first
467         // request gets thrown away.
468         reply2 := &vpe.ControlPingReply{}
469         err := reqCtx2.ReceiveReply(reply2)
470         Expect(err).To(BeNil())
471         Expect(reply2.Retval).To(BeEquivalentTo(2))
472
473         // first request has already been considered closed
474         reply1 := &vpe.ControlPingReply{}
475         err = reqCtx1.ReceiveReply(reply1)
476         Expect(err).ToNot(BeNil())
477         Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
478 }
479
480 func TestCycleOverSetOfSequenceNumbers(t *testing.T) {
481         ctx := setupTest(t, true)
482         defer ctx.teardownTest()
483
484         numIters := 0xffff + 100
485         reqCtx := make(map[int]*api.RequestCtx)
486
487         var seqNum uint16 = 0
488         for i := 0; i < numIters + 30 /* receiver is 30 reqs behind */; i++ {
489                 seqNum++
490                 if i < numIters {
491                         ctx.mockVpp.MockReplyWithSeqNum(&vpe.ControlPingReply{Retval: int32(i)}, false, seqNum)
492                         req := &vpe.ControlPing{}
493                         reqCtx[i] = ctx.ch.SendRequest(req)
494                 }
495                 if i > 30 {
496                         reply := &vpe.ControlPingReply{}
497                         err := reqCtx[i-30].ReceiveReply(reply)
498                         Expect(err).ShouldNot(HaveOccurred())
499                         Expect(reply.Retval).To(BeEquivalentTo(i-30))
500                 }
501         }
502 }