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.
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/stats"
28 "git.fd.io/govpp.git/codec"
29 . "github.com/onsi/gomega"
33 mockVpp *mock.VppAdapter
38 func setupTest(t *testing.T, bufferedChan bool) *testCtx {
42 ctx.mockVpp = &mock.VppAdapter{}
45 ctx.conn, err = core.Connect(ctx.mockVpp)
46 Expect(err).ShouldNot(HaveOccurred())
49 ctx.ch, err = ctx.conn.NewAPIChannelBuffered(100, 100)
51 ctx.ch, err = ctx.conn.NewAPIChannel()
53 Expect(err).ShouldNot(HaveOccurred())
58 func (ctx *testCtx) teardownTest() {
63 func TestSimpleRequest(t *testing.T) {
64 ctx := setupTest(t, false)
65 defer ctx.teardownTest()
67 ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: -5})
69 req := &vpe.ControlPing{}
70 reply := &vpe.ControlPingReply{}
72 // send the request and receive a reply
73 ctx.ch.GetRequestChannel() <- &api.VppRequest{Message: req}
74 vppReply := <-ctx.ch.GetReplyChannel()
76 Expect(vppReply).ShouldNot(BeNil())
77 Expect(vppReply.Error).ShouldNot(HaveOccurred())
80 err := ctx.ch.GetMessageDecoder().DecodeMsg(vppReply.Data, reply)
81 Expect(err).ShouldNot(HaveOccurred())
83 Expect(reply.Retval).To(BeEquivalentTo(-5))
86 func TestMultiRequest(t *testing.T) {
87 ctx := setupTest(t, false)
88 defer ctx.teardownTest()
90 msgs := []api.Message{}
91 for m := 0; m < 10; m++ {
92 msgs = append(msgs, &interfaces.SwInterfaceDetails{})
94 ctx.mockVpp.MockReply(msgs...)
95 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
97 // send multipart request
98 ctx.ch.GetRequestChannel() <- &api.VppRequest{Message: &interfaces.SwInterfaceDump{}, Multipart: true}
103 vppReply := <-ctx.ch.GetReplyChannel()
104 if vppReply.LastReplyReceived {
105 break // break out of the loop
107 Expect(vppReply.Error).ShouldNot(HaveOccurred())
109 // decode the message
110 reply := &interfaces.SwInterfaceDetails{}
111 err := ctx.ch.GetMessageDecoder().DecodeMsg(vppReply.Data, reply)
112 Expect(err).ShouldNot(HaveOccurred())
116 Expect(cnt).To(BeEquivalentTo(10))
119 func TestNotifications(t *testing.T) {
120 ctx := setupTest(t, false)
121 defer ctx.teardownTest()
123 // subscribe for notification
124 notifChan := make(chan api.Message, 1)
125 subscription := &api.NotifSubscription{
126 NotifChan: notifChan,
127 MsgFactory: interfaces.NewSwInterfaceSetFlags,
129 ctx.ch.GetNotificationChannel() <- &api.NotifSubscribeRequest{
130 Subscription: subscription,
133 err := <-ctx.ch.GetNotificationReplyChannel()
134 Expect(err).ShouldNot(HaveOccurred())
136 // mock the notification and force its delivery
137 ctx.mockVpp.MockReply(&interfaces.SwInterfaceSetFlags{
141 ctx.mockVpp.SendMsg(0, []byte{0})
143 // receive the notification
144 notif := (<-notifChan).(*interfaces.SwInterfaceSetFlags)
146 Expect(notif.SwIfIndex).To(BeEquivalentTo(3))
148 // unsubscribe notification
149 ctx.ch.GetNotificationChannel() <- &api.NotifSubscribeRequest{
150 Subscription: subscription,
153 err = <-ctx.ch.GetNotificationReplyChannel()
154 Expect(err).ShouldNot(HaveOccurred())
157 func TestNilConnection(t *testing.T) {
159 var conn *core.Connection
161 ch, err := conn.NewAPIChannel()
162 Expect(ch).Should(BeNil())
163 Expect(err).Should(HaveOccurred())
164 Expect(err.Error()).To(ContainSubstring("nil"))
166 ch, err = conn.NewAPIChannelBuffered(1, 1)
167 Expect(ch).Should(BeNil())
168 Expect(err).Should(HaveOccurred())
169 Expect(err.Error()).To(ContainSubstring("nil"))
172 func TestDoubleConnection(t *testing.T) {
173 ctx := setupTest(t, false)
174 defer ctx.teardownTest()
176 conn, err := core.Connect(ctx.mockVpp)
177 Expect(err).Should(HaveOccurred())
178 Expect(err.Error()).To(ContainSubstring("only one connection per process"))
179 Expect(conn).Should(BeNil())
182 func TestAsyncConnection(t *testing.T) {
183 ctx := setupTest(t, false)
184 defer ctx.teardownTest()
186 ctx.conn.Disconnect()
187 conn, ch, err := core.AsyncConnect(ctx.mockVpp)
190 Expect(err).ShouldNot(HaveOccurred())
191 Expect(conn).ShouldNot(BeNil())
194 Expect(ev.State).Should(BeEquivalentTo(core.Connected))
197 func TestFullBuffer(t *testing.T) {
198 ctx := setupTest(t, false)
199 defer ctx.teardownTest()
201 // close the default API channel
204 // create a new channel with limited buffer sizes
206 ctx.ch, err = ctx.conn.NewAPIChannelBuffered(10, 1)
207 Expect(err).ShouldNot(HaveOccurred())
209 // send multiple requests, only one reply should be read
210 for i := 0; i < 20; i++ {
211 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
212 ctx.ch.GetRequestChannel() <- &api.VppRequest{Message: &vpe.ControlPing{}}
215 vppReply := <-ctx.ch.GetReplyChannel()
216 Expect(vppReply).ShouldNot(BeNil())
220 case <-ctx.ch.GetReplyChannel():
221 received = true // this should not happen
223 received = false // no reply to be received
225 Expect(received).Should(BeFalse(), "A reply has been recieved, should had been ignored.")
228 func TestCodec(t *testing.T) {
231 msgCodec := &codec.MsgCodec{}
234 data, err := msgCodec.EncodeMsg(&interfaces.CreateLoopback{MacAddress: []byte{1, 2, 3, 4, 5, 6}}, 11)
235 Expect(err).ShouldNot(HaveOccurred())
236 Expect(data).ShouldNot(BeEmpty())
238 msg1 := &interfaces.CreateLoopback{}
239 err = msgCodec.DecodeMsg(data, msg1)
240 Expect(err).ShouldNot(HaveOccurred())
241 Expect(msg1.MacAddress).To(BeEquivalentTo([]byte{1, 2, 3, 4, 5, 6}))
244 data, err = msgCodec.EncodeMsg(&vpe.ControlPingReply{Retval: 55}, 22)
245 Expect(err).ShouldNot(HaveOccurred())
246 Expect(data).ShouldNot(BeEmpty())
248 msg2 := &vpe.ControlPingReply{}
249 err = msgCodec.DecodeMsg(data, msg2)
250 Expect(err).ShouldNot(HaveOccurred())
251 Expect(msg2.Retval).To(BeEquivalentTo(55))
254 data, err = msgCodec.EncodeMsg(&stats.VnetIP4FibCounters{VrfID: 77}, 33)
255 Expect(err).ShouldNot(HaveOccurred())
256 Expect(data).ShouldNot(BeEmpty())
258 msg3 := &stats.VnetIP4FibCounters{}
259 err = msgCodec.DecodeMsg(data, msg3)
260 Expect(err).ShouldNot(HaveOccurred())
261 Expect(msg3.VrfID).To(BeEquivalentTo(77))
264 func TestCodecNegative(t *testing.T) {
267 msgCodec := &codec.MsgCodec{}
269 // nil message for encoding
270 data, err := msgCodec.EncodeMsg(nil, 15)
271 Expect(err).Should(HaveOccurred())
272 Expect(err.Error()).To(ContainSubstring("nil message"))
273 Expect(data).Should(BeNil())
275 // nil message for decoding
276 err = msgCodec.DecodeMsg(data, nil)
277 Expect(err).Should(HaveOccurred())
278 Expect(err.Error()).To(ContainSubstring("nil message"))
280 // nil data for decoding
281 err = msgCodec.DecodeMsg(nil, &vpe.ControlPingReply{})
282 Expect(err).Should(HaveOccurred())
283 Expect(err.Error()).To(ContainSubstring("EOF"))
286 func TestSimpleRequestsWithSequenceNumbers(t *testing.T) {
287 ctx := setupTest(t, false)
288 defer ctx.teardownTest()
290 var reqCtx []api.RequestCtx
291 for i := 0; i < 10; i++ {
292 ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: int32(i)})
293 req := &vpe.ControlPing{}
294 reqCtx = append(reqCtx, ctx.ch.SendRequest(req))
297 for i := 0; i < 10; i++ {
298 reply := &vpe.ControlPingReply{}
299 err := reqCtx[i].ReceiveReply(reply)
300 Expect(err).ShouldNot(HaveOccurred())
301 Expect(reply.Retval).To(BeEquivalentTo(i))
305 func TestMultiRequestsWithSequenceNumbers(t *testing.T) {
306 ctx := setupTest(t, false)
307 defer ctx.teardownTest()
309 msgs := []api.Message{}
310 for i := 0; i < 10; i++ {
311 msgs = append(msgs, &interfaces.SwInterfaceDetails{SwIfIndex: uint32(i)})
313 ctx.mockVpp.MockReply(msgs...)
314 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
316 // send multipart request
317 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
321 Expect(cnt < 11).To(BeTrue())
324 reply := &interfaces.SwInterfaceDetails{}
325 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
327 if lastReplyReceived {
328 break // break out of the loop
331 Expect(err).ShouldNot(HaveOccurred())
332 Expect(reply.SwIfIndex).To(BeEquivalentTo(cnt))
337 Expect(cnt).To(BeEquivalentTo(10))
340 func TestSimpleRequestWithTimeout(t *testing.T) {
341 ctx := setupTest(t, true)
342 defer ctx.teardownTest()
344 // reply for a previous timeouted requests to be ignored
345 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
346 Msg: &vpe.ControlPingReply{Retval: 1},
351 req1 := &vpe.ControlPing{}
352 reqCtx1 := ctx.ch.SendRequest(req1)
354 reply := &vpe.ControlPingReply{}
355 err := reqCtx1.ReceiveReply(reply)
356 Expect(err).ToNot(BeNil())
357 Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
359 ctx.mockVpp.MockReplyWithContext(
360 // reply for the previous request
362 Msg: &vpe.ControlPingReply{Retval: 1},
365 // reply for the next request
367 Msg: &vpe.ControlPingReply{Retval: 2},
372 req2 := &vpe.ControlPing{}
373 reqCtx2 := ctx.ch.SendRequest(req2)
375 // second request should ignore the first reply and return the second one
376 reply = &vpe.ControlPingReply{}
377 err = reqCtx2.ReceiveReply(reply)
378 Expect(err).To(BeNil())
379 Expect(reply.Retval).To(BeEquivalentTo(2))
382 func TestSimpleRequestsWithMissingReply(t *testing.T) {
383 ctx := setupTest(t, false)
384 defer ctx.teardownTest()
386 // request without reply
387 req1 := &vpe.ControlPing{}
388 reqCtx1 := ctx.ch.SendRequest(req1)
390 // another request without reply
391 req2 := &vpe.ControlPing{}
392 reqCtx2 := ctx.ch.SendRequest(req2)
394 // third request with reply
395 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
396 Msg: &vpe.ControlPingReply{Retval: 3},
399 req3 := &vpe.ControlPing{}
400 reqCtx3 := ctx.ch.SendRequest(req3)
402 // the first two should fail, but not consume reply for the 3rd
403 reply := &vpe.ControlPingReply{}
404 err := reqCtx1.ReceiveReply(reply)
405 Expect(err).ToNot(BeNil())
406 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
408 reply = &vpe.ControlPingReply{}
409 err = reqCtx2.ReceiveReply(reply)
410 Expect(err).ToNot(BeNil())
411 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 2"))
413 // the second request should succeed
414 reply = &vpe.ControlPingReply{}
415 err = reqCtx3.ReceiveReply(reply)
416 Expect(err).To(BeNil())
417 Expect(reply.Retval).To(BeEquivalentTo(3))
420 func TestMultiRequestsWithErrors(t *testing.T) {
421 ctx := setupTest(t, false)
422 defer ctx.teardownTest()
424 // replies for a previous timeouted requests to be ignored
425 msgs := []mock.MsgWithContext{}
428 Msg: &vpe.ControlPingReply{Retval: 1},
432 Msg: &vpe.ControlPingReply{Retval: 1},
436 Msg: &vpe.ControlPingReply{Retval: 1},
440 for i := 0; i < 10; i++ {
443 Msg: &interfaces.SwInterfaceDetails{SwIfIndex: uint32(i)},
448 // missing finalizing control ping
450 // reply for a next request
453 Msg: &vpe.ControlPingReply{Retval: 2},
459 ctx.mockVpp.MockReplyWithContext(msgs...)
461 // send multipart request
462 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
464 for i := 0; i < 10; i++ {
465 // receive multi-part replies
466 reply := &interfaces.SwInterfaceDetails{}
467 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
469 Expect(lastReplyReceived).To(BeFalse())
470 Expect(err).ShouldNot(HaveOccurred())
471 Expect(reply.SwIfIndex).To(BeEquivalentTo(i))
474 // missing closing control ping
475 reply := &interfaces.SwInterfaceDetails{}
476 _, err := reqCtx.ReceiveReply(reply)
477 Expect(err).ToNot(BeNil())
478 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
480 // try again - still fails and nothing consumed
481 _, err = reqCtx.ReceiveReply(reply)
482 Expect(err).ToNot(BeNil())
483 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
485 // reply for the second request has not been consumed
486 reqCtx2 := ctx.ch.SendRequest(&vpe.ControlPing{})
487 reply2 := &vpe.ControlPingReply{}
488 err = reqCtx2.ReceiveReply(reply2)
489 Expect(err).To(BeNil())
490 Expect(reply2.Retval).To(BeEquivalentTo(2))
493 func TestRequestsOrdering(t *testing.T) {
494 ctx := setupTest(t, false)
495 defer ctx.teardownTest()
497 // the orderings of SendRequest and ReceiveReply calls should match, otherwise
498 // some replies will get thrown away
501 ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: 1})
502 req1 := &vpe.ControlPing{}
503 reqCtx1 := ctx.ch.SendRequest(req1)
506 ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: 2})
507 req2 := &vpe.ControlPing{}
508 reqCtx2 := ctx.ch.SendRequest(req2)
510 // if reply for the second request is read first, the reply for the first
511 // request gets thrown away.
512 reply2 := &vpe.ControlPingReply{}
513 err := reqCtx2.ReceiveReply(reply2)
514 Expect(err).To(BeNil())
515 Expect(reply2.Retval).To(BeEquivalentTo(2))
517 // first request has already been considered closed
518 reply1 := &vpe.ControlPingReply{}
519 err = reqCtx1.ReceiveReply(reply1)
520 Expect(err).ToNot(BeNil())
521 Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
524 func TestCycleOverSetOfSequenceNumbers(t *testing.T) {
525 ctx := setupTest(t, true)
526 defer ctx.teardownTest()
528 numIters := 0xffff + 100
529 reqCtx := make(map[int]api.RequestCtx)
531 for i := 0; i < numIters+30; i++ {
533 ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: int32(i)})
534 req := &vpe.ControlPing{}
535 reqCtx[i] = ctx.ch.SendRequest(req)
538 reply := &vpe.ControlPingReply{}
539 err := reqCtx[i-30].ReceiveReply(reply)
540 Expect(err).ShouldNot(HaveOccurred())
541 Expect(reply.Retval).To(BeEquivalentTo(i - 30))