1 // Copyright (c) 2017 Cisco and/or its affiliates.
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:
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
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"
27 "git.fd.io/govpp.git/codec"
28 . "github.com/onsi/gomega"
32 mockVpp *mock.VppAdapter
37 func setupTest(t *testing.T, bufferedChan bool) *testCtx {
41 ctx.mockVpp = &mock.VppAdapter{}
44 ctx.conn, err = core.Connect(ctx.mockVpp)
45 Expect(err).ShouldNot(HaveOccurred())
48 ctx.ch, err = ctx.conn.NewAPIChannelBuffered(100, 100)
50 ctx.ch, err = ctx.conn.NewAPIChannel()
52 Expect(err).ShouldNot(HaveOccurred())
57 func (ctx *testCtx) teardownTest() {
62 func TestSimpleRequest(t *testing.T) {
63 ctx := setupTest(t, false)
64 defer ctx.teardownTest()
66 ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: -5})
68 req := &vpe.ControlPing{}
69 reply := &vpe.ControlPingReply{}
71 // send the request and receive a reply
72 ctx.ch.GetRequestChannel() <- &api.VppRequest{Message: req}
73 vppReply := <-ctx.ch.GetReplyChannel()
75 Expect(vppReply).ShouldNot(BeNil())
76 Expect(vppReply.Error).ShouldNot(HaveOccurred())
79 err := ctx.ch.GetMessageDecoder().DecodeMsg(vppReply.Data, reply)
80 Expect(err).ShouldNot(HaveOccurred())
82 Expect(reply.Retval).To(BeEquivalentTo(-5))
85 func TestMultiRequest(t *testing.T) {
86 ctx := setupTest(t, false)
87 defer ctx.teardownTest()
89 msgs := []api.Message{}
90 for m := 0; m < 10; m++ {
91 msgs = append(msgs, &interfaces.SwInterfaceDetails{})
93 ctx.mockVpp.MockReply(msgs...)
94 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
96 // send multipart request
97 ctx.ch.GetRequestChannel() <- &api.VppRequest{Message: &interfaces.SwInterfaceDump{}, Multipart: true}
102 vppReply := <-ctx.ch.GetReplyChannel()
103 if vppReply.LastReplyReceived {
104 break // break out of the loop
106 Expect(vppReply.Error).ShouldNot(HaveOccurred())
108 // decode the message
109 reply := &interfaces.SwInterfaceDetails{}
110 err := ctx.ch.GetMessageDecoder().DecodeMsg(vppReply.Data, reply)
111 Expect(err).ShouldNot(HaveOccurred())
115 Expect(cnt).To(BeEquivalentTo(10))
118 func TestNotifications(t *testing.T) {
119 ctx := setupTest(t, false)
120 defer ctx.teardownTest()
122 // subscribe for notification
123 notifChan := make(chan api.Message, 1)
124 subscription := &api.NotifSubscription{
125 NotifChan: notifChan,
126 MsgFactory: interfaces.NewSwInterfaceSetFlags,
128 ctx.ch.GetNotificationChannel() <- &api.NotifSubscribeRequest{
129 Subscription: subscription,
132 err := <-ctx.ch.GetNotificationReplyChannel()
133 Expect(err).ShouldNot(HaveOccurred())
135 // mock the notification and force its delivery
136 ctx.mockVpp.MockReply(&interfaces.SwInterfaceSetFlags{
140 ctx.mockVpp.SendMsg(0, []byte{0})
142 // receive the notification
143 notif := (<-notifChan).(*interfaces.SwInterfaceSetFlags)
145 Expect(notif.SwIfIndex).To(BeEquivalentTo(3))
147 // unsubscribe notification
148 ctx.ch.GetNotificationChannel() <- &api.NotifSubscribeRequest{
149 Subscription: subscription,
152 err = <-ctx.ch.GetNotificationReplyChannel()
153 Expect(err).ShouldNot(HaveOccurred())
156 func TestNilConnection(t *testing.T) {
158 var conn *core.Connection
160 ch, err := conn.NewAPIChannel()
161 Expect(ch).Should(BeNil())
162 Expect(err).Should(HaveOccurred())
163 Expect(err.Error()).To(ContainSubstring("nil"))
165 ch, err = conn.NewAPIChannelBuffered(1, 1)
166 Expect(ch).Should(BeNil())
167 Expect(err).Should(HaveOccurred())
168 Expect(err.Error()).To(ContainSubstring("nil"))
171 func TestDoubleConnection(t *testing.T) {
172 ctx := setupTest(t, false)
173 defer ctx.teardownTest()
175 conn, err := core.Connect(ctx.mockVpp)
176 Expect(err).Should(HaveOccurred())
177 Expect(err.Error()).To(ContainSubstring("only one connection per process"))
178 Expect(conn).Should(BeNil())
181 func TestAsyncConnection(t *testing.T) {
182 ctx := setupTest(t, false)
183 defer ctx.teardownTest()
185 ctx.conn.Disconnect()
186 conn, ch, err := core.AsyncConnect(ctx.mockVpp)
189 Expect(err).ShouldNot(HaveOccurred())
190 Expect(conn).ShouldNot(BeNil())
193 Expect(ev.State).Should(BeEquivalentTo(core.Connected))
196 func TestFullBuffer(t *testing.T) {
197 ctx := setupTest(t, false)
198 defer ctx.teardownTest()
200 // close the default API channel
203 // create a new channel with limited buffer sizes
205 ctx.ch, err = ctx.conn.NewAPIChannelBuffered(10, 1)
206 Expect(err).ShouldNot(HaveOccurred())
208 // send multiple requests, only one reply should be read
209 for i := 0; i < 20; i++ {
210 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
211 ctx.ch.GetRequestChannel() <- &api.VppRequest{Message: &vpe.ControlPing{}}
214 vppReply := <-ctx.ch.GetReplyChannel()
215 Expect(vppReply).ShouldNot(BeNil())
219 case <-ctx.ch.GetReplyChannel():
220 received = true // this should not happen
222 received = false // no reply to be received
224 Expect(received).Should(BeFalse(), "A reply has been recieved, should had been ignored.")
227 func TestCodec(t *testing.T) {
230 msgCodec := &codec.MsgCodec{}
233 data, err := msgCodec.EncodeMsg(&interfaces.CreateLoopback{MacAddress: []byte{1, 2, 3, 4, 5, 6}}, 11)
234 Expect(err).ShouldNot(HaveOccurred())
235 Expect(data).ShouldNot(BeEmpty())
237 msg1 := &interfaces.CreateLoopback{}
238 err = msgCodec.DecodeMsg(data, msg1)
239 Expect(err).ShouldNot(HaveOccurred())
240 Expect(msg1.MacAddress).To(BeEquivalentTo([]byte{1, 2, 3, 4, 5, 6}))
243 data, err = msgCodec.EncodeMsg(&vpe.ControlPingReply{Retval: 55}, 22)
244 Expect(err).ShouldNot(HaveOccurred())
245 Expect(data).ShouldNot(BeEmpty())
247 msg2 := &vpe.ControlPingReply{}
248 err = msgCodec.DecodeMsg(data, msg2)
249 Expect(err).ShouldNot(HaveOccurred())
250 Expect(msg2.Retval).To(BeEquivalentTo(55))
253 data, err = msgCodec.EncodeMsg(&stats.VnetIP4FibCounters{VrfID: 77}, 33)
254 Expect(err).ShouldNot(HaveOccurred())
255 Expect(data).ShouldNot(BeEmpty())
257 msg3 := &stats.VnetIP4FibCounters{}
258 err = msgCodec.DecodeMsg(data, msg3)
259 Expect(err).ShouldNot(HaveOccurred())
260 Expect(msg3.VrfID).To(BeEquivalentTo(77))
263 func TestCodecNegative(t *testing.T) {
266 msgCodec := &codec.MsgCodec{}
268 // nil message for encoding
269 data, err := msgCodec.EncodeMsg(nil, 15)
270 Expect(err).Should(HaveOccurred())
271 Expect(err.Error()).To(ContainSubstring("nil message"))
272 Expect(data).Should(BeNil())
274 // nil message for decoding
275 err = msgCodec.DecodeMsg(data, nil)
276 Expect(err).Should(HaveOccurred())
277 Expect(err.Error()).To(ContainSubstring("nil message"))
279 // nil data for decoding
280 err = msgCodec.DecodeMsg(nil, &vpe.ControlPingReply{})
281 Expect(err).Should(HaveOccurred())
282 Expect(err.Error()).To(ContainSubstring("EOF"))
285 func TestSimpleRequestsWithSequenceNumbers(t *testing.T) {
286 ctx := setupTest(t, false)
287 defer ctx.teardownTest()
289 var reqCtx []api.RequestCtx
290 for i := 0; i < 10; i++ {
291 ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: int32(i)})
292 req := &vpe.ControlPing{}
293 reqCtx = append(reqCtx, ctx.ch.SendRequest(req))
296 for i := 0; i < 10; i++ {
297 reply := &vpe.ControlPingReply{}
298 err := reqCtx[i].ReceiveReply(reply)
299 Expect(err).ShouldNot(HaveOccurred())
300 Expect(reply.Retval).To(BeEquivalentTo(i))
304 func TestMultiRequestsWithSequenceNumbers(t *testing.T) {
305 ctx := setupTest(t, false)
306 defer ctx.teardownTest()
308 msgs := []api.Message{}
309 for i := 0; i < 10; i++ {
310 msgs = append(msgs, &interfaces.SwInterfaceDetails{SwIfIndex: uint32(i)})
312 ctx.mockVpp.MockReply(msgs...)
313 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
315 // send multipart request
316 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
320 Expect(cnt < 11).To(BeTrue())
323 reply := &interfaces.SwInterfaceDetails{}
324 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
326 if lastReplyReceived {
327 break // break out of the loop
330 Expect(err).ShouldNot(HaveOccurred())
331 Expect(reply.SwIfIndex).To(BeEquivalentTo(cnt))
336 Expect(cnt).To(BeEquivalentTo(10))
339 func TestSimpleRequestWithTimeout(t *testing.T) {
340 ctx := setupTest(t, true)
341 defer ctx.teardownTest()
343 // reply for a previous timeouted requests to be ignored
344 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
345 Msg: &vpe.ControlPingReply{Retval: 1},
350 req1 := &vpe.ControlPing{}
351 reqCtx1 := ctx.ch.SendRequest(req1)
353 reply := &vpe.ControlPingReply{}
354 err := reqCtx1.ReceiveReply(reply)
355 Expect(err).ToNot(BeNil())
356 Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
358 ctx.mockVpp.MockReplyWithContext(
359 // reply for the previous request
361 Msg: &vpe.ControlPingReply{Retval: 1},
364 // reply for the next request
366 Msg: &vpe.ControlPingReply{Retval: 2},
371 req2 := &vpe.ControlPing{}
372 reqCtx2 := ctx.ch.SendRequest(req2)
374 // second request should ignore the first reply and return the second one
375 reply = &vpe.ControlPingReply{}
376 err = reqCtx2.ReceiveReply(reply)
377 Expect(err).To(BeNil())
378 Expect(reply.Retval).To(BeEquivalentTo(2))
381 func TestSimpleRequestsWithMissingReply(t *testing.T) {
382 ctx := setupTest(t, false)
383 defer ctx.teardownTest()
385 // request without reply
386 req1 := &vpe.ControlPing{}
387 reqCtx1 := ctx.ch.SendRequest(req1)
389 // another request without reply
390 req2 := &vpe.ControlPing{}
391 reqCtx2 := ctx.ch.SendRequest(req2)
393 // third request with reply
394 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
395 Msg: &vpe.ControlPingReply{Retval: 3},
398 req3 := &vpe.ControlPing{}
399 reqCtx3 := ctx.ch.SendRequest(req3)
401 // the first two should fail, but not consume reply for the 3rd
402 reply := &vpe.ControlPingReply{}
403 err := reqCtx1.ReceiveReply(reply)
404 Expect(err).ToNot(BeNil())
405 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
407 reply = &vpe.ControlPingReply{}
408 err = reqCtx2.ReceiveReply(reply)
409 Expect(err).ToNot(BeNil())
410 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 2"))
412 // the second request should succeed
413 reply = &vpe.ControlPingReply{}
414 err = reqCtx3.ReceiveReply(reply)
415 Expect(err).To(BeNil())
416 Expect(reply.Retval).To(BeEquivalentTo(3))
419 func TestMultiRequestsWithErrors(t *testing.T) {
420 ctx := setupTest(t, false)
421 defer ctx.teardownTest()
423 // replies for a previous timeouted requests to be ignored
424 msgs := []mock.MsgWithContext{}
427 Msg: &vpe.ControlPingReply{Retval: 1},
431 Msg: &vpe.ControlPingReply{Retval: 1},
435 Msg: &vpe.ControlPingReply{Retval: 1},
439 for i := 0; i < 10; i++ {
442 Msg: &interfaces.SwInterfaceDetails{SwIfIndex: uint32(i)},
447 // missing finalizing control ping
449 // reply for a next request
452 Msg: &vpe.ControlPingReply{Retval: 2},
458 ctx.mockVpp.MockReplyWithContext(msgs...)
460 // send multipart request
461 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
463 for i := 0; i < 10; i++ {
464 // receive multi-part replies
465 reply := &interfaces.SwInterfaceDetails{}
466 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
468 Expect(lastReplyReceived).To(BeFalse())
469 Expect(err).ShouldNot(HaveOccurred())
470 Expect(reply.SwIfIndex).To(BeEquivalentTo(i))
473 // missing closing control ping
474 reply := &interfaces.SwInterfaceDetails{}
475 _, err := reqCtx.ReceiveReply(reply)
476 Expect(err).ToNot(BeNil())
477 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
479 // try again - still fails and nothing consumed
480 _, err = reqCtx.ReceiveReply(reply)
481 Expect(err).ToNot(BeNil())
482 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
484 // reply for the second request has not been consumed
485 reqCtx2 := ctx.ch.SendRequest(&vpe.ControlPing{})
486 reply2 := &vpe.ControlPingReply{}
487 err = reqCtx2.ReceiveReply(reply2)
488 Expect(err).To(BeNil())
489 Expect(reply2.Retval).To(BeEquivalentTo(2))
492 func TestRequestsOrdering(t *testing.T) {
493 ctx := setupTest(t, false)
494 defer ctx.teardownTest()
496 // the orderings of SendRequest and ReceiveReply calls should match, otherwise
497 // some replies will get thrown away
500 ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: 1})
501 req1 := &vpe.ControlPing{}
502 reqCtx1 := ctx.ch.SendRequest(req1)
505 ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: 2})
506 req2 := &vpe.ControlPing{}
507 reqCtx2 := ctx.ch.SendRequest(req2)
509 // if reply for the second request is read first, the reply for the first
510 // request gets thrown away.
511 reply2 := &vpe.ControlPingReply{}
512 err := reqCtx2.ReceiveReply(reply2)
513 Expect(err).To(BeNil())
514 Expect(reply2.Retval).To(BeEquivalentTo(2))
516 // first request has already been considered closed
517 reply1 := &vpe.ControlPingReply{}
518 err = reqCtx1.ReceiveReply(reply1)
519 Expect(err).ToNot(BeNil())
520 Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
523 func TestCycleOverSetOfSequenceNumbers(t *testing.T) {
524 ctx := setupTest(t, true)
525 defer ctx.teardownTest()
527 numIters := 0xffff + 100
528 reqCtx := make(map[int]api.RequestCtx)
530 for i := 0; i < numIters+30; /* receiver is 30 reqs behind */ i++ {
532 ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: int32(i)})
533 req := &vpe.ControlPing{}
534 reqCtx[i] = ctx.ch.SendRequest(req)
537 reply := &vpe.ControlPingReply{}
538 err := reqCtx[i-30].ReceiveReply(reply)
539 Expect(err).ShouldNot(HaveOccurred())
540 Expect(reply.Retval).To(BeEquivalentTo(i - 30))