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 . "github.com/onsi/gomega"
31 mockVpp *mock.VppAdapter
36 func setupTest(t *testing.T, bufferedChan bool) *testCtx {
40 ctx.mockVpp = &mock.VppAdapter{}
43 ctx.conn, err = core.Connect(ctx.mockVpp)
44 Expect(err).ShouldNot(HaveOccurred())
47 ctx.ch, err = ctx.conn.NewAPIChannelBuffered(100, 100)
49 ctx.ch, err = ctx.conn.NewAPIChannel()
51 Expect(err).ShouldNot(HaveOccurred())
56 func (ctx *testCtx) teardownTest() {
61 func TestSimpleRequest(t *testing.T) {
62 ctx := setupTest(t, false)
63 defer ctx.teardownTest()
65 ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: -5})
67 req := &vpe.ControlPing{}
68 reply := &vpe.ControlPingReply{}
70 // send the request and receive a reply
71 ctx.ch.ReqChan <- &api.VppRequest{Message: req}
72 vppReply := <-ctx.ch.ReplyChan
74 Expect(vppReply).ShouldNot(BeNil())
75 Expect(vppReply.Error).ShouldNot(HaveOccurred())
78 err := ctx.ch.MsgDecoder.DecodeMsg(vppReply.Data, reply)
79 Expect(err).ShouldNot(HaveOccurred())
81 Expect(reply.Retval).To(BeEquivalentTo(-5))
84 func TestMultiRequest(t *testing.T) {
85 ctx := setupTest(t, false)
86 defer ctx.teardownTest()
88 msgs := []api.Message{}
89 for m := 0; m < 10; m++ {
90 msgs = append(msgs, &interfaces.SwInterfaceDetails{})
92 ctx.mockVpp.MockReply(msgs...)
93 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
95 // send multipart request
96 ctx.ch.ReqChan <- &api.VppRequest{Message: &interfaces.SwInterfaceDump{}, Multipart: true}
101 vppReply := <-ctx.ch.ReplyChan
102 if vppReply.LastReplyReceived {
103 break // break out of the loop
105 Expect(vppReply.Error).ShouldNot(HaveOccurred())
107 // decode the message
108 reply := &interfaces.SwInterfaceDetails{}
109 err := ctx.ch.MsgDecoder.DecodeMsg(vppReply.Data, reply)
110 Expect(err).ShouldNot(HaveOccurred())
114 Expect(cnt).To(BeEquivalentTo(10))
117 func TestNotifications(t *testing.T) {
118 ctx := setupTest(t, false)
119 defer ctx.teardownTest()
121 // subscribe for notification
122 notifChan := make(chan api.Message, 1)
123 subscription := &api.NotifSubscription{
124 NotifChan: notifChan,
125 MsgFactory: interfaces.NewSwInterfaceSetFlags,
127 ctx.ch.NotifSubsChan <- &api.NotifSubscribeRequest{
128 Subscription: subscription,
131 err := <-ctx.ch.NotifSubsReplyChan
132 Expect(err).ShouldNot(HaveOccurred())
134 // mock the notification and force its delivery
135 ctx.mockVpp.MockReply(&interfaces.SwInterfaceSetFlags{
139 ctx.mockVpp.SendMsg(0, []byte{0})
141 // receive the notification
142 notif := (<-notifChan).(*interfaces.SwInterfaceSetFlags)
144 Expect(notif.SwIfIndex).To(BeEquivalentTo(3))
146 // unsubscribe notification
147 ctx.ch.NotifSubsChan <- &api.NotifSubscribeRequest{
148 Subscription: subscription,
151 err = <-ctx.ch.NotifSubsReplyChan
152 Expect(err).ShouldNot(HaveOccurred())
155 func TestNilConnection(t *testing.T) {
157 var conn *core.Connection
159 ch, err := conn.NewAPIChannel()
160 Expect(ch).Should(BeNil())
161 Expect(err).Should(HaveOccurred())
162 Expect(err.Error()).To(ContainSubstring("nil"))
164 ch, err = conn.NewAPIChannelBuffered(1, 1)
165 Expect(ch).Should(BeNil())
166 Expect(err).Should(HaveOccurred())
167 Expect(err.Error()).To(ContainSubstring("nil"))
170 func TestDoubleConnection(t *testing.T) {
171 ctx := setupTest(t, false)
172 defer ctx.teardownTest()
174 conn, err := core.Connect(ctx.mockVpp)
175 Expect(err).Should(HaveOccurred())
176 Expect(err.Error()).To(ContainSubstring("only one connection per process"))
177 Expect(conn).Should(BeNil())
180 func TestAsyncConnection(t *testing.T) {
181 ctx := setupTest(t, false)
182 defer ctx.teardownTest()
184 ctx.conn.Disconnect()
185 conn, ch, err := core.AsyncConnect(ctx.mockVpp)
188 Expect(err).ShouldNot(HaveOccurred())
189 Expect(conn).ShouldNot(BeNil())
192 Expect(ev.State).Should(BeEquivalentTo(core.Connected))
195 func TestFullBuffer(t *testing.T) {
196 ctx := setupTest(t, false)
197 defer ctx.teardownTest()
199 // close the default API channel
202 // create a new channel with limited buffer sizes
204 ctx.ch, err = ctx.conn.NewAPIChannelBuffered(10, 1)
205 Expect(err).ShouldNot(HaveOccurred())
207 // send multiple requests, only one reply should be read
208 for i := 0; i < 20; i++ {
209 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
210 ctx.ch.ReqChan <- &api.VppRequest{Message: &vpe.ControlPing{}}
213 vppReply := <-ctx.ch.ReplyChan
214 Expect(vppReply).ShouldNot(BeNil())
218 case <-ctx.ch.ReplyChan:
219 received = true // this should not happen
221 received = false // no reply to be received
223 Expect(received).Should(BeFalse(), "A reply has been recieved, should had been ignored.")
226 func TestCodec(t *testing.T) {
229 codec := &core.MsgCodec{}
232 data, err := codec.EncodeMsg(&interfaces.CreateLoopback{MacAddress: []byte{1, 2, 3, 4, 5, 6}}, 11)
233 Expect(err).ShouldNot(HaveOccurred())
234 Expect(data).ShouldNot(BeEmpty())
236 msg1 := &interfaces.CreateLoopback{}
237 err = codec.DecodeMsg(data, msg1)
238 Expect(err).ShouldNot(HaveOccurred())
239 Expect(msg1.MacAddress).To(BeEquivalentTo([]byte{1, 2, 3, 4, 5, 6}))
242 data, err = codec.EncodeMsg(&vpe.ControlPingReply{Retval: 55}, 22)
243 Expect(err).ShouldNot(HaveOccurred())
244 Expect(data).ShouldNot(BeEmpty())
246 msg2 := &vpe.ControlPingReply{}
247 err = codec.DecodeMsg(data, msg2)
248 Expect(err).ShouldNot(HaveOccurred())
249 Expect(msg2.Retval).To(BeEquivalentTo(55))
252 data, err = codec.EncodeMsg(&stats.VnetIP4FibCounters{VrfID: 77}, 33)
253 Expect(err).ShouldNot(HaveOccurred())
254 Expect(data).ShouldNot(BeEmpty())
256 msg3 := &stats.VnetIP4FibCounters{}
257 err = codec.DecodeMsg(data, msg3)
258 Expect(err).ShouldNot(HaveOccurred())
259 Expect(msg3.VrfID).To(BeEquivalentTo(77))
262 func TestCodecNegative(t *testing.T) {
265 codec := &core.MsgCodec{}
267 // nil message for encoding
268 data, err := codec.EncodeMsg(nil, 15)
269 Expect(err).Should(HaveOccurred())
270 Expect(err.Error()).To(ContainSubstring("nil message"))
271 Expect(data).Should(BeNil())
273 // nil message for decoding
274 err = codec.DecodeMsg(data, nil)
275 Expect(err).Should(HaveOccurred())
276 Expect(err.Error()).To(ContainSubstring("nil message"))
278 // nil data for decoding
279 err = codec.DecodeMsg(nil, &vpe.ControlPingReply{})
280 Expect(err).Should(HaveOccurred())
281 Expect(err.Error()).To(ContainSubstring("EOF"))
284 func TestSimpleRequestsWithSequenceNumbers(t *testing.T) {
285 ctx := setupTest(t, false)
286 defer ctx.teardownTest()
288 var reqCtx []*api.RequestCtx
289 for i := 0; i < 10; i++ {
290 ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: int32(i)})
291 req := &vpe.ControlPing{}
292 reqCtx = append(reqCtx, ctx.ch.SendRequest(req))
295 for i := 0; i < 10; i++ {
296 reply := &vpe.ControlPingReply{}
297 err := reqCtx[i].ReceiveReply(reply)
298 Expect(err).ShouldNot(HaveOccurred())
299 Expect(reply.Retval).To(BeEquivalentTo(i))
303 func TestMultiRequestsWithSequenceNumbers(t *testing.T) {
304 ctx := setupTest(t, false)
305 defer ctx.teardownTest()
307 msgs := []api.Message{}
308 for i := 0; i < 10; i++ {
309 msgs = append(msgs, &interfaces.SwInterfaceDetails{SwIfIndex: uint32(i)})
311 ctx.mockVpp.MockReply(msgs...)
312 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
314 // send multipart request
315 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
319 Expect(cnt < 11).To(BeTrue())
322 reply := &interfaces.SwInterfaceDetails{}
323 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
325 if lastReplyReceived {
326 break // break out of the loop
329 Expect(err).ShouldNot(HaveOccurred())
330 Expect(reply.SwIfIndex).To(BeEquivalentTo(cnt))
335 Expect(cnt).To(BeEquivalentTo(10))
338 func TestSimpleRequestWithTimeout(t *testing.T) {
339 ctx := setupTest(t, true)
340 defer ctx.teardownTest()
342 // reply for a previous timeouted requests to be ignored
343 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
344 Msg: &vpe.ControlPingReply{Retval: 1},
349 req1 := &vpe.ControlPing{}
350 reqCtx1 := ctx.ch.SendRequest(req1)
352 reply := &vpe.ControlPingReply{}
353 err := reqCtx1.ReceiveReply(reply)
354 Expect(err).ToNot(BeNil())
355 Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
357 ctx.mockVpp.MockReplyWithContext(
358 // reply for the previous request
360 Msg: &vpe.ControlPingReply{Retval: 1},
363 // reply for the next request
365 Msg: &vpe.ControlPingReply{Retval: 2},
370 req2 := &vpe.ControlPing{}
371 reqCtx2 := ctx.ch.SendRequest(req2)
373 // second request should ignore the first reply and return the second one
374 reply = &vpe.ControlPingReply{}
375 err = reqCtx2.ReceiveReply(reply)
376 Expect(err).To(BeNil())
377 Expect(reply.Retval).To(BeEquivalentTo(2))
380 func TestSimpleRequestsWithMissingReply(t *testing.T) {
381 ctx := setupTest(t, false)
382 defer ctx.teardownTest()
384 // request without reply
385 req1 := &vpe.ControlPing{}
386 reqCtx1 := ctx.ch.SendRequest(req1)
388 // another request without reply
389 req2 := &vpe.ControlPing{}
390 reqCtx2 := ctx.ch.SendRequest(req2)
392 // third request with reply
393 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
394 Msg: &vpe.ControlPingReply{Retval: 3},
397 req3 := &vpe.ControlPing{}
398 reqCtx3 := ctx.ch.SendRequest(req3)
400 // the first two should fail, but not consume reply for the 3rd
401 reply := &vpe.ControlPingReply{}
402 err := reqCtx1.ReceiveReply(reply)
403 Expect(err).ToNot(BeNil())
404 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
406 reply = &vpe.ControlPingReply{}
407 err = reqCtx2.ReceiveReply(reply)
408 Expect(err).ToNot(BeNil())
409 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 2"))
411 // the second request should succeed
412 reply = &vpe.ControlPingReply{}
413 err = reqCtx3.ReceiveReply(reply)
414 Expect(err).To(BeNil())
415 Expect(reply.Retval).To(BeEquivalentTo(3))
418 func TestMultiRequestsWithErrors(t *testing.T) {
419 ctx := setupTest(t, false)
420 defer ctx.teardownTest()
422 // replies for a previous timeouted requests to be ignored
423 msgs := []mock.MsgWithContext{}
426 Msg: &vpe.ControlPingReply{Retval: 1},
430 Msg: &vpe.ControlPingReply{Retval: 1},
434 Msg: &vpe.ControlPingReply{Retval: 1},
438 for i := 0; i < 10; i++ {
441 Msg: &interfaces.SwInterfaceDetails{SwIfIndex: uint32(i)},
446 // missing finalizing control ping
448 // reply for a next request
451 Msg: &vpe.ControlPingReply{Retval: 2},
457 ctx.mockVpp.MockReplyWithContext(msgs...)
459 // send multipart request
460 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
462 for i := 0; i < 10; i++ {
463 // receive multi-part replies
464 reply := &interfaces.SwInterfaceDetails{}
465 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
467 Expect(lastReplyReceived).To(BeFalse())
468 Expect(err).ShouldNot(HaveOccurred())
469 Expect(reply.SwIfIndex).To(BeEquivalentTo(i))
472 // missing closing control ping
473 reply := &interfaces.SwInterfaceDetails{}
474 _, err := reqCtx.ReceiveReply(reply)
475 Expect(err).ToNot(BeNil())
476 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
478 // try again - still fails and nothing consumed
479 _, err = reqCtx.ReceiveReply(reply)
480 Expect(err).ToNot(BeNil())
481 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
483 // reply for the second request has not been consumed
484 reqCtx2 := ctx.ch.SendRequest(&vpe.ControlPing{})
485 reply2 := &vpe.ControlPingReply{}
486 err = reqCtx2.ReceiveReply(reply2)
487 Expect(err).To(BeNil())
488 Expect(reply2.Retval).To(BeEquivalentTo(2))
491 func TestRequestsOrdering(t *testing.T) {
492 ctx := setupTest(t, false)
493 defer ctx.teardownTest()
495 // the orderings of SendRequest and ReceiveReply calls should match, otherwise
496 // some replies will get thrown away
499 ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: 1})
500 req1 := &vpe.ControlPing{}
501 reqCtx1 := ctx.ch.SendRequest(req1)
504 ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: 2})
505 req2 := &vpe.ControlPing{}
506 reqCtx2 := ctx.ch.SendRequest(req2)
508 // if reply for the second request is read first, the reply for the first
509 // request gets thrown away.
510 reply2 := &vpe.ControlPingReply{}
511 err := reqCtx2.ReceiveReply(reply2)
512 Expect(err).To(BeNil())
513 Expect(reply2.Retval).To(BeEquivalentTo(2))
515 // first request has already been considered closed
516 reply1 := &vpe.ControlPingReply{}
517 err = reqCtx1.ReceiveReply(reply1)
518 Expect(err).ToNot(BeNil())
519 Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
522 func TestCycleOverSetOfSequenceNumbers(t *testing.T) {
523 ctx := setupTest(t, true)
524 defer ctx.teardownTest()
526 numIters := 0xffff + 100
527 reqCtx := make(map[int]*api.RequestCtx)
529 for i := 0; i < numIters+30; /* receiver is 30 reqs behind */ i++ {
531 ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: int32(i)})
532 req := &vpe.ControlPing{}
533 reqCtx[i] = ctx.ch.SendRequest(req)
536 reply := &vpe.ControlPingReply{}
537 err := reqCtx[i-30].ReceiveReply(reply)
538 Expect(err).ShouldNot(HaveOccurred())
539 Expect(reply.Retval).To(BeEquivalentTo(i - 30))