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}, false)
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 for m := 0; m < 10; m++ {
89 ctx.mockVpp.MockReply(&interfaces.SwInterfaceDetails{}, true)
91 ctx.mockVpp.MockReply(&vpe.ControlPingReply{}, true)
93 // send multipart request
94 ctx.ch.ReqChan <- &api.VppRequest{Message: &interfaces.SwInterfaceDump{}, Multipart: true}
99 vppReply := <-ctx.ch.ReplyChan
100 if vppReply.LastReplyReceived {
101 break // break out of the loop
103 Expect(vppReply.Error).ShouldNot(HaveOccurred())
105 // decode the message
106 reply := &interfaces.SwInterfaceDetails{}
107 err := ctx.ch.MsgDecoder.DecodeMsg(vppReply.Data, reply)
108 Expect(err).ShouldNot(HaveOccurred())
112 Expect(cnt).To(BeEquivalentTo(10))
115 func TestNotifications(t *testing.T) {
116 ctx := setupTest(t, false)
117 defer ctx.teardownTest()
119 // subscribe for notification
120 notifChan := make(chan api.Message, 1)
121 subscription := &api.NotifSubscription{
122 NotifChan: notifChan,
123 MsgFactory: interfaces.NewSwInterfaceSetFlags,
125 ctx.ch.NotifSubsChan <- &api.NotifSubscribeRequest{
126 Subscription: subscription,
129 err := <-ctx.ch.NotifSubsReplyChan
130 Expect(err).ShouldNot(HaveOccurred())
132 // mock the notification and force its delivery
133 ctx.mockVpp.MockReply(&interfaces.SwInterfaceSetFlags{
137 ctx.mockVpp.SendMsg(0, []byte{0})
139 // receive the notification
140 notif := (<-notifChan).(*interfaces.SwInterfaceSetFlags)
142 Expect(notif.SwIfIndex).To(BeEquivalentTo(3))
144 // unsubscribe notification
145 ctx.ch.NotifSubsChan <- &api.NotifSubscribeRequest{
146 Subscription: subscription,
149 err = <-ctx.ch.NotifSubsReplyChan
150 Expect(err).ShouldNot(HaveOccurred())
153 func TestNilConnection(t *testing.T) {
155 var conn *core.Connection
157 ch, err := conn.NewAPIChannel()
158 Expect(ch).Should(BeNil())
159 Expect(err).Should(HaveOccurred())
160 Expect(err.Error()).To(ContainSubstring("nil"))
162 ch, err = conn.NewAPIChannelBuffered(1, 1)
163 Expect(ch).Should(BeNil())
164 Expect(err).Should(HaveOccurred())
165 Expect(err.Error()).To(ContainSubstring("nil"))
168 func TestDoubleConnection(t *testing.T) {
169 ctx := setupTest(t, false)
170 defer ctx.teardownTest()
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())
178 func TestAsyncConnection(t *testing.T) {
179 ctx := setupTest(t, false)
180 defer ctx.teardownTest()
182 ctx.conn.Disconnect()
183 conn, ch, err := core.AsyncConnect(ctx.mockVpp)
186 Expect(err).ShouldNot(HaveOccurred())
187 Expect(conn).ShouldNot(BeNil())
190 Expect(ev.State).Should(BeEquivalentTo(core.Connected))
193 func TestFullBuffer(t *testing.T) {
194 ctx := setupTest(t, false)
195 defer ctx.teardownTest()
197 // close the default API channel
200 // create a new channel with limited buffer sizes
202 ctx.ch, err = ctx.conn.NewAPIChannelBuffered(10, 1)
203 Expect(err).ShouldNot(HaveOccurred())
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{}}
211 vppReply := <-ctx.ch.ReplyChan
212 Expect(vppReply).ShouldNot(BeNil())
216 case <-ctx.ch.ReplyChan:
217 received = true // this should not happen
219 received = false // no reply to be received
221 Expect(received).Should(BeFalse(), "A reply has been recieved, should had been ignored.")
224 func TestCodec(t *testing.T) {
227 codec := &core.MsgCodec{}
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())
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}))
240 data, err = codec.EncodeMsg(&vpe.ControlPingReply{Retval: 55}, 22)
241 Expect(err).ShouldNot(HaveOccurred())
242 Expect(data).ShouldNot(BeEmpty())
244 msg2 := &vpe.ControlPingReply{}
245 err = codec.DecodeMsg(data, msg2)
246 Expect(err).ShouldNot(HaveOccurred())
247 Expect(msg2.Retval).To(BeEquivalentTo(55))
250 data, err = codec.EncodeMsg(&stats.VnetIP4FibCounters{VrfID: 77}, 33)
251 Expect(err).ShouldNot(HaveOccurred())
252 Expect(data).ShouldNot(BeEmpty())
254 msg3 := &stats.VnetIP4FibCounters{}
255 err = codec.DecodeMsg(data, msg3)
256 Expect(err).ShouldNot(HaveOccurred())
257 Expect(msg3.VrfID).To(BeEquivalentTo(77))
260 func TestCodecNegative(t *testing.T) {
263 codec := &core.MsgCodec{}
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())
271 // nil message for decoding
272 err = codec.DecodeMsg(data, nil)
273 Expect(err).Should(HaveOccurred())
274 Expect(err.Error()).To(ContainSubstring("nil message"))
276 // nil data for decoding
277 err = codec.DecodeMsg(nil, &vpe.ControlPingReply{})
278 Expect(err).Should(HaveOccurred())
279 Expect(err.Error()).To(ContainSubstring("EOF"))
282 func TestSimpleRequestsWithSequenceNumbers(t *testing.T) {
283 ctx := setupTest(t, false)
284 defer ctx.teardownTest()
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))
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))
301 func TestMultiRequestsWithSequenceNumbers(t *testing.T) {
302 ctx := setupTest(t, false)
303 defer ctx.teardownTest()
305 for i := 0; i < 10; i++ {
306 ctx.mockVpp.MockReply(&interfaces.SwInterfaceDetails{SwIfIndex: uint32(i)}, true)
308 ctx.mockVpp.MockReply(&vpe.ControlPingReply{}, true)
310 // send multipart request
311 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
315 Expect(cnt < 11).To(BeTrue())
318 reply := &interfaces.SwInterfaceDetails{}
319 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
321 if lastReplyReceived {
322 break // break out of the loop
325 Expect(err).ShouldNot(HaveOccurred())
326 Expect(reply.SwIfIndex).To(BeEquivalentTo(cnt))
331 Expect(cnt).To(BeEquivalentTo(10))
334 func TestSimpleRequestWithTimeout(t *testing.T) {
335 ctx := setupTest(t, true)
336 defer ctx.teardownTest()
338 // reply for a previous timeouted requests to be ignored
339 ctx.mockVpp.MockReplyWithSeqNum(&vpe.ControlPingReply{Retval: 1}, false,0)
342 req1 := &vpe.ControlPing{}
343 reqCtx1 := ctx.ch.SendRequest(req1)
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"))
350 // reply for the previous request
351 ctx.mockVpp.MockReplyWithSeqNum(&vpe.ControlPingReply{Retval: 1}, false,1)
354 ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: 2}, false)
355 req2 := &vpe.ControlPing{}
356 reqCtx2 := ctx.ch.SendRequest(req2)
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))
365 func TestSimpleRequestsWithMissingReply(t *testing.T) {
366 ctx := setupTest(t, false)
367 defer ctx.teardownTest()
369 // request without reply
370 req1 := &vpe.ControlPing{}
371 reqCtx1 := ctx.ch.SendRequest(req1)
373 // another request without reply
374 req2 := &vpe.ControlPing{}
375 reqCtx2 := ctx.ch.SendRequest(req2)
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)
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"))
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"))
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))
400 func TestMultiRequestsWithErrors(t *testing.T) {
401 ctx := setupTest(t, false)
402 defer ctx.teardownTest()
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)
409 for i := 0; i < 10; i++ {
410 ctx.mockVpp.MockReply(&interfaces.SwInterfaceDetails{SwIfIndex: uint32(i)}, true)
412 // missing finalizing control ping
414 // reply for a next request
415 ctx.mockVpp.MockReplyWithSeqNum(&vpe.ControlPingReply{Retval: 2}, false,2)
417 // send multipart request
418 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
420 for i := 0; i < 10; i++ {
421 // receive multi-part replies
422 reply := &interfaces.SwInterfaceDetails{}
423 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
425 Expect(lastReplyReceived).To(BeFalse())
426 Expect(err).ShouldNot(HaveOccurred())
427 Expect(reply.SwIfIndex).To(BeEquivalentTo(i))
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"))
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"))
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))
449 func TestRequestsOrdering(t *testing.T) {
450 ctx := setupTest(t, false)
451 defer ctx.teardownTest()
453 // the orderings of SendRequest and ReceiveReply calls should match, otherwise
454 // some replies will get thrown away
457 ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: 1}, false)
458 req1 := &vpe.ControlPing{}
459 reqCtx1 := ctx.ch.SendRequest(req1)
462 ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: 2}, false)
463 req2 := &vpe.ControlPing{}
464 reqCtx2 := ctx.ch.SendRequest(req2)
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))
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"))
480 func TestCycleOverSetOfSequenceNumbers(t *testing.T) {
481 ctx := setupTest(t, true)
482 defer ctx.teardownTest()
484 numIters := 0xffff + 100
485 reqCtx := make(map[int]*api.RequestCtx)
487 var seqNum uint16 = 0
488 for i := 0; i < numIters + 30 /* receiver is 30 reqs behind */; i++ {
491 ctx.mockVpp.MockReplyWithSeqNum(&vpe.ControlPingReply{Retval: int32(i)}, false, seqNum)
492 req := &vpe.ControlPing{}
493 reqCtx[i] = ctx.ch.SendRequest(req)
496 reply := &vpe.ControlPingReply{}
497 err := reqCtx[i-30].ReceiveReply(reply)
498 Expect(err).ShouldNot(HaveOccurred())
499 Expect(reply.Retval).To(BeEquivalentTo(i-30))