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/core/bin_api/vpe"
23 "git.fd.io/govpp.git/examples/bin_api/interfaces"
24 "git.fd.io/govpp.git/examples/bin_api/memif"
25 "git.fd.io/govpp.git/examples/bin_api/tap"
27 "git.fd.io/govpp.git/api"
28 . "github.com/onsi/gomega"
32 mockVpp *mock.VppAdapter
37 func setupTest(t *testing.T) *testCtx {
41 mockVpp: &mock.VppAdapter{},
45 ctx.conn, err = Connect(ctx.mockVpp)
46 Expect(err).ShouldNot(HaveOccurred())
48 ctx.ch, err = ctx.conn.NewAPIChannel()
49 Expect(err).ShouldNot(HaveOccurred())
54 func (ctx *testCtx) teardownTest() {
59 func TestRequestReplyTapConnect(t *testing.T) {
61 defer ctx.teardownTest()
63 ctx.mockVpp.MockReply(&tap.TapConnectReply{
67 request := &tap.TapConnect{
68 TapName: []byte("test-tap-name"),
71 reply := &tap.TapConnectReply{}
73 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
74 Expect(err).ShouldNot(HaveOccurred())
75 Expect(reply.Retval).To(BeEquivalentTo(10), "Incorrect retval value for TapConnectReply")
76 Expect(reply.SwIfIndex).To(BeEquivalentTo(1), "Incorrect SwIfIndex value for TapConnectReply")
79 func TestRequestReplyTapModify(t *testing.T) {
81 defer ctx.teardownTest()
83 ctx.mockVpp.MockReply(&tap.TapModifyReply{
87 request := &tap.TapModify{
88 TapName: []byte("test-tap-modify"),
92 reply := &tap.TapModifyReply{}
94 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
95 Expect(err).ShouldNot(HaveOccurred())
96 Expect(reply.Retval).To(BeEquivalentTo(15), "Incorrect retval value for TapModifyReply")
97 Expect(reply.SwIfIndex).To(BeEquivalentTo(2), "Incorrect SwIfIndex value for TapModifyReply")
100 func TestRequestReplyTapDelete(t *testing.T) {
102 defer ctx.teardownTest()
104 ctx.mockVpp.MockReply(&tap.TapDeleteReply{
107 request := &tap.TapDelete{
110 reply := &tap.TapDeleteReply{}
112 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
113 Expect(err).ShouldNot(HaveOccurred())
114 Expect(reply.Retval).To(BeEquivalentTo(20), "Incorrect retval value for TapDeleteReply")
117 func TestRequestReplySwInterfaceTapDump(t *testing.T) {
119 defer ctx.teardownTest()
121 byteName := []byte("dev-name-test")
122 ctx.mockVpp.MockReply(&tap.SwInterfaceTapDetails{
126 request := &tap.SwInterfaceTapDump{}
127 reply := &tap.SwInterfaceTapDetails{}
129 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
130 Expect(err).ShouldNot(HaveOccurred())
131 Expect(reply.SwIfIndex).To(BeEquivalentTo(25), "Incorrect SwIfIndex value for SwInterfaceTapDetails")
132 Expect(reply.DevName).ToNot(BeNil(), "Incorrect DevName value for SwInterfaceTapDetails")
135 func TestRequestReplyMemifCreate(t *testing.T) {
137 defer ctx.teardownTest()
139 ctx.mockVpp.MockReply(&memif.MemifCreateReply{
143 request := &memif.MemifCreate{
149 reply := &memif.MemifCreateReply{}
151 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
152 Expect(err).ShouldNot(HaveOccurred())
153 Expect(reply.Retval).To(BeEquivalentTo(22), "Incorrect Retval value for MemifCreate")
154 Expect(reply.SwIfIndex).To(BeEquivalentTo(4), "Incorrect SwIfIndex value for MemifCreate")
157 func TestRequestReplyMemifDelete(t *testing.T) {
159 defer ctx.teardownTest()
161 ctx.mockVpp.MockReply(&memif.MemifDeleteReply{
164 request := &memif.MemifDelete{
167 reply := &memif.MemifDeleteReply{}
169 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
170 Expect(err).ShouldNot(HaveOccurred())
171 Expect(reply.Retval).To(BeEquivalentTo(24), "Incorrect Retval value for MemifDelete")
174 func TestRequestReplyMemifDetails(t *testing.T) {
176 defer ctx.teardownTest()
178 ctx.mockVpp.MockReply(&memif.MemifDetails{
180 IfName: []byte("memif-name"),
183 request := &memif.MemifDump{}
184 reply := &memif.MemifDetails{}
186 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
187 Expect(err).ShouldNot(HaveOccurred())
188 Expect(reply.SwIfIndex).To(BeEquivalentTo(25), "Incorrect SwIfIndex value for MemifDetails")
189 Expect(reply.IfName).ToNot(BeEmpty(), "MemifDetails IfName is empty byte array")
190 Expect(reply.Role).To(BeEquivalentTo(0), "Incorrect Role value for MemifDetails")
193 func TestMultiRequestReplySwInterfaceTapDump(t *testing.T) {
195 defer ctx.teardownTest()
198 var msgs []api.Message
199 for i := 1; i <= 10; i++ {
200 msgs = append(msgs, &tap.SwInterfaceTapDetails{
201 SwIfIndex: uint32(i),
202 DevName: []byte("dev-name-test"),
205 ctx.mockVpp.MockReply(msgs...)
206 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
208 reqCtx := ctx.ch.SendMultiRequest(&tap.SwInterfaceTapDump{})
211 msg := &tap.SwInterfaceTapDetails{}
212 stop, err := reqCtx.ReceiveReply(msg)
214 break // break out of the loop
216 Expect(err).ShouldNot(HaveOccurred())
219 Expect(cnt).To(BeEquivalentTo(10))
222 func TestMultiRequestReplySwInterfaceMemifDump(t *testing.T) {
224 defer ctx.teardownTest()
227 var msgs []api.Message
228 for i := 1; i <= 10; i++ {
229 msgs = append(msgs, &memif.MemifDetails{
230 SwIfIndex: uint32(i),
233 ctx.mockVpp.MockReply(msgs...)
234 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
236 reqCtx := ctx.ch.SendMultiRequest(&memif.MemifDump{})
239 msg := &memif.MemifDetails{}
240 stop, err := reqCtx.ReceiveReply(msg)
242 break // break out of the loop
244 Expect(err).ShouldNot(HaveOccurred())
247 Expect(cnt).To(BeEquivalentTo(10))
250 func TestNotifications(t *testing.T) {
252 defer ctx.teardownTest()
254 // subscribe for notification
255 notifChan := make(chan api.Message, 1)
256 subs, err := ctx.ch.SubscribeNotification(notifChan, interfaces.NewSwInterfaceSetFlags)
257 Expect(err).ShouldNot(HaveOccurred())
259 // mock the notification and force its delivery
260 ctx.mockVpp.MockReply(&interfaces.SwInterfaceSetFlags{
264 ctx.mockVpp.SendMsg(0, []byte(""))
266 // receive the notification
267 var notif *interfaces.SwInterfaceSetFlags
268 Eventually(func() *interfaces.SwInterfaceSetFlags {
270 case n := <-notifChan:
271 notif = n.(*interfaces.SwInterfaceSetFlags)
276 }).ShouldNot(BeNil())
278 // verify the received notifications
279 Expect(notif.SwIfIndex).To(BeEquivalentTo(3), "Incorrect SwIfIndex value for SwInterfaceSetFlags")
280 Expect(notif.AdminUpDown).To(BeEquivalentTo(1), "Incorrect AdminUpDown value for SwInterfaceSetFlags")
282 ctx.ch.UnsubscribeNotification(subs)
285 func TestNotificationEvent(t *testing.T) {
287 defer ctx.teardownTest()
289 // subscribe for notification
290 notifChan := make(chan api.Message, 1)
291 subs, err := ctx.ch.SubscribeNotification(notifChan, interfaces.NewSwInterfaceEvent)
292 Expect(err).ShouldNot(HaveOccurred())
294 // mock the notification and force its delivery
295 ctx.mockVpp.MockReply(&interfaces.SwInterfaceEvent{
299 ctx.mockVpp.SendMsg(0, []byte(""))
301 // receive the notification
302 var notif *interfaces.SwInterfaceEvent
303 Eventually(func() *interfaces.SwInterfaceEvent {
305 case n := <-notifChan:
306 notif = n.(*interfaces.SwInterfaceEvent)
311 }).ShouldNot(BeNil())
313 // verify the received notifications
314 Expect(notif.SwIfIndex).To(BeEquivalentTo(2), "Incorrect SwIfIndex value for SwInterfaceSetFlags")
315 Expect(notif.LinkUpDown).To(BeEquivalentTo(1), "Incorrect LinkUpDown value for SwInterfaceSetFlags")
317 ctx.ch.UnsubscribeNotification(subs)
320 func TestCheckMessageCompatibility(t *testing.T) {
322 defer ctx.teardownTest()
324 err := ctx.ch.CheckMessageCompatibility(&interfaces.SwInterfaceSetFlags{})
325 Expect(err).ShouldNot(HaveOccurred())
327 func TestSetReplyTimeout(t *testing.T) {
329 defer ctx.teardownTest()
331 ctx.ch.SetReplyTimeout(time.Millisecond)
333 // first one request should work
334 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
335 err := ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
336 Expect(err).ShouldNot(HaveOccurred())
338 // no other reply ready - expect timeout
339 err = ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
340 Expect(err).Should(HaveOccurred())
341 Expect(err.Error()).To(ContainSubstring("timeout"))
344 func TestSetReplyTimeoutMultiRequest(t *testing.T) {
346 defer ctx.teardownTest()
348 ctx.ch.SetReplyTimeout(time.Millisecond)
350 var msgs []api.Message
351 for i := 1; i <= 3; i++ {
352 msgs = append(msgs, &interfaces.SwInterfaceDetails{
353 SwIfIndex: uint32(i),
354 InterfaceName: []byte("if-name-test"),
357 ctx.mockVpp.MockReply(msgs...)
358 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
361 sendMultiRequest := func() error {
362 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
364 msg := &interfaces.SwInterfaceDetails{}
365 stop, err := reqCtx.ReceiveReply(msg)
367 break // break out of the loop
377 // first one request should work
378 err := sendMultiRequest()
379 Expect(err).ShouldNot(HaveOccurred())
381 // no other reply ready - expect timeout
382 err = sendMultiRequest()
383 Expect(err).Should(HaveOccurred())
384 Expect(err.Error()).To(ContainSubstring("timeout"))
386 Expect(cnt).To(BeEquivalentTo(3))
389 func TestReceiveReplyNegative(t *testing.T) {
391 defer ctx.teardownTest()
394 reqCtx1 := &requestCtxData{}
395 err := reqCtx1.ReceiveReply(&vpe.ControlPingReply{})
396 Expect(err).Should(HaveOccurred())
397 Expect(err.Error()).To(ContainSubstring("invalid request context"))
400 reqCtx2 := &multiRequestCtxData{}
401 _, err = reqCtx2.ReceiveReply(&vpe.ControlPingReply{})
402 Expect(err).Should(HaveOccurred())
403 Expect(err.Error()).To(ContainSubstring("invalid request context"))
406 reqCtx3 := &requestCtxData{}
407 err = reqCtx3.ReceiveReply(nil)
408 Expect(err).Should(HaveOccurred())
409 Expect(err.Error()).To(ContainSubstring("invalid request context"))
412 func TestMultiRequestDouble(t *testing.T) {
414 defer ctx.teardownTest()
417 var msgs []mock.MsgWithContext
418 for i := 1; i <= 3; i++ {
419 msgs = append(msgs, mock.MsgWithContext{
420 Msg: &interfaces.SwInterfaceDetails{
421 SwIfIndex: uint32(i),
422 InterfaceName: []byte("if-name-test"),
428 msgs = append(msgs, mock.MsgWithContext{Msg: &vpe.ControlPingReply{}, Multipart: true, SeqNum: 1})
430 for i := 1; i <= 3; i++ {
433 Msg: &interfaces.SwInterfaceDetails{
434 SwIfIndex: uint32(i),
435 InterfaceName: []byte("if-name-test"),
441 msgs = append(msgs, mock.MsgWithContext{Msg: &vpe.ControlPingReply{}, Multipart: true, SeqNum: 2})
443 ctx.mockVpp.MockReplyWithContext(msgs...)
446 var sendMultiRequest = func() error {
447 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
449 msg := &interfaces.SwInterfaceDetails{}
450 stop, err := reqCtx.ReceiveReply(msg)
452 break // break out of the loop
462 err := sendMultiRequest()
463 Expect(err).ShouldNot(HaveOccurred())
465 err = sendMultiRequest()
466 Expect(err).ShouldNot(HaveOccurred())
468 Expect(cnt).To(BeEquivalentTo(6))
471 func TestReceiveReplyAfterTimeout(t *testing.T) {
473 defer ctx.teardownTest()
475 ctx.ch.SetReplyTimeout(time.Millisecond)
477 // first one request should work
478 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &vpe.ControlPingReply{}, SeqNum: 1})
479 err := ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
480 Expect(err).ShouldNot(HaveOccurred())
482 err = ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
483 Expect(err).Should(HaveOccurred())
484 Expect(err.Error()).To(ContainSubstring("timeout"))
486 ctx.mockVpp.MockReplyWithContext(
487 // simulating late reply
488 mock.MsgWithContext{Msg: &vpe.ControlPingReply{}, SeqNum: 2},
489 // normal reply for next request
490 mock.MsgWithContext{Msg: &tap.TapConnectReply{}, SeqNum: 3})
492 req := &tap.TapConnect{
493 TapName: []byte("test-tap-name"),
496 reply := &tap.TapConnectReply{}
499 err = ctx.ch.SendRequest(req).ReceiveReply(reply)
500 Expect(err).ShouldNot(HaveOccurred())
503 func TestReceiveReplyAfterTimeoutMultiRequest(t *testing.T) {
505 TODO: fix mock adapter
506 This test will fail because mock adapter will stop sending replies
507 when it encounters control_ping_reply from multi request,
508 thus never sending reply for next request
513 defer ctx.teardownTest()
515 ctx.ch.SetReplyTimeout(time.Millisecond * 100)
517 // first one request should work
518 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &vpe.ControlPingReply{}, SeqNum: 1})
519 err := ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
520 Expect(err).ShouldNot(HaveOccurred())
523 var sendMultiRequest = func() error {
524 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
526 msg := &interfaces.SwInterfaceDetails{}
527 stop, err := reqCtx.ReceiveReply(msg)
529 break // break out of the loop
539 err = sendMultiRequest()
540 Expect(err).Should(HaveOccurred())
541 Expect(err.Error()).To(ContainSubstring("timeout"))
542 Expect(cnt).To(BeEquivalentTo(0))
544 // simulating late replies
545 var msgs []mock.MsgWithContext
546 for i := 1; i <= 3; i++ {
547 msgs = append(msgs, mock.MsgWithContext{
548 Msg: &interfaces.SwInterfaceDetails{
549 SwIfIndex: uint32(i),
550 InterfaceName: []byte("if-name-test"),
556 msgs = append(msgs, mock.MsgWithContext{Msg: &vpe.ControlPingReply{}, Multipart: true, SeqNum: 2})
557 ctx.mockVpp.MockReplyWithContext(msgs...)
559 // normal reply for next request
560 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &tap.TapConnectReply{}, SeqNum: 3})
562 req := &tap.TapConnect{
563 TapName: []byte("test-tap-name"),
566 reply := &tap.TapConnectReply{}
569 err = ctx.ch.SendRequest(req).ReceiveReply(reply)
570 Expect(err).ShouldNot(HaveOccurred())
573 func TestInvalidMessageID(t *testing.T) {
575 defer ctx.teardownTest()
577 // first one request should work
578 ctx.mockVpp.MockReply(&vpe.ShowVersionReply{})
579 err := ctx.ch.SendRequest(&vpe.ShowVersion{}).ReceiveReply(&vpe.ShowVersionReply{})
580 Expect(err).ShouldNot(HaveOccurred())
582 // second should fail with error invalid message ID
583 ctx.mockVpp.MockReply(&vpe.ShowVersionReply{})
584 err = ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
585 Expect(err).Should(HaveOccurred())
586 Expect(err.Error()).To(ContainSubstring("invalid message ID"))