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/memif"
27 "git.fd.io/govpp.git/examples/bin_api/tap"
29 . "github.com/onsi/gomega"
33 mockVpp *mock.VppAdapter
38 func setupTest(t *testing.T) *testCtx {
42 mockVpp: &mock.VppAdapter{},
46 ctx.conn, err = core.Connect(ctx.mockVpp)
47 Expect(err).ShouldNot(HaveOccurred())
49 ctx.ch, err = ctx.conn.NewAPIChannel()
50 Expect(err).ShouldNot(HaveOccurred())
55 func (ctx *testCtx) teardownTest() {
60 func TestRequestReplyTapConnect(t *testing.T) {
62 defer ctx.teardownTest()
64 ctx.mockVpp.MockReply(&tap.TapConnectReply{
68 request := &tap.TapConnect{
69 TapName: []byte("test-tap-name"),
72 reply := &tap.TapConnectReply{}
74 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
75 Expect(err).ShouldNot(HaveOccurred())
76 Expect(reply.Retval).To(BeEquivalentTo(10), "Incorrect retval value for TapConnectReply")
77 Expect(reply.SwIfIndex).To(BeEquivalentTo(1), "Incorrect SwIfIndex value for TapConnectReply")
80 func TestRequestReplyTapModify(t *testing.T) {
82 defer ctx.teardownTest()
84 ctx.mockVpp.MockReply(&tap.TapModifyReply{
88 request := &tap.TapModify{
89 TapName: []byte("test-tap-modify"),
93 reply := &tap.TapModifyReply{}
95 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
96 Expect(err).ShouldNot(HaveOccurred())
97 Expect(reply.Retval).To(BeEquivalentTo(15), "Incorrect retval value for TapModifyReply")
98 Expect(reply.SwIfIndex).To(BeEquivalentTo(2), "Incorrect SwIfIndex value for TapModifyReply")
101 func TestRequestReplyTapDelete(t *testing.T) {
103 defer ctx.teardownTest()
105 ctx.mockVpp.MockReply(&tap.TapDeleteReply{
108 request := &tap.TapDelete{
111 reply := &tap.TapDeleteReply{}
113 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
114 Expect(err).ShouldNot(HaveOccurred())
115 Expect(reply.Retval).To(BeEquivalentTo(20), "Incorrect retval value for TapDeleteReply")
118 func TestRequestReplySwInterfaceTapDump(t *testing.T) {
120 defer ctx.teardownTest()
122 byteName := []byte("dev-name-test")
123 ctx.mockVpp.MockReply(&tap.SwInterfaceTapDetails{
127 request := &tap.SwInterfaceTapDump{}
128 reply := &tap.SwInterfaceTapDetails{}
130 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
131 Expect(err).ShouldNot(HaveOccurred())
132 Expect(reply.SwIfIndex).To(BeEquivalentTo(25), "Incorrect SwIfIndex value for SwInterfaceTapDetails")
133 Expect(reply.DevName).ToNot(BeNil(), "Incorrect DevName value for SwInterfaceTapDetails")
136 func TestRequestReplyMemifCreate(t *testing.T) {
138 defer ctx.teardownTest()
140 ctx.mockVpp.MockReply(&memif.MemifCreateReply{
144 request := &memif.MemifCreate{
150 reply := &memif.MemifCreateReply{}
152 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
153 Expect(err).ShouldNot(HaveOccurred())
154 Expect(reply.Retval).To(BeEquivalentTo(22), "Incorrect Retval value for MemifCreate")
155 Expect(reply.SwIfIndex).To(BeEquivalentTo(4), "Incorrect SwIfIndex value for MemifCreate")
158 func TestRequestReplyMemifDelete(t *testing.T) {
160 defer ctx.teardownTest()
162 ctx.mockVpp.MockReply(&memif.MemifDeleteReply{
165 request := &memif.MemifDelete{
168 reply := &memif.MemifDeleteReply{}
170 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
171 Expect(err).ShouldNot(HaveOccurred())
172 Expect(reply.Retval).To(BeEquivalentTo(24), "Incorrect Retval value for MemifDelete")
175 func TestRequestReplyMemifDetails(t *testing.T) {
177 defer ctx.teardownTest()
179 ctx.mockVpp.MockReply(&memif.MemifDetails{
181 IfName: []byte("memif-name"),
184 request := &memif.MemifDump{}
185 reply := &memif.MemifDetails{}
187 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
188 Expect(err).ShouldNot(HaveOccurred())
189 Expect(reply.SwIfIndex).To(BeEquivalentTo(25), "Incorrect SwIfIndex value for MemifDetails")
190 Expect(reply.IfName).ToNot(BeEmpty(), "MemifDetails IfName is empty byte array")
191 Expect(reply.Role).To(BeEquivalentTo(0), "Incorrect Role value for MemifDetails")
194 func TestMultiRequestReplySwInterfaceTapDump(t *testing.T) {
196 defer ctx.teardownTest()
199 msgs := []api.Message{}
200 for i := 1; i <= 10; i++ {
201 msgs = append(msgs, &tap.SwInterfaceTapDetails{
202 SwIfIndex: uint32(i),
203 DevName: []byte("dev-name-test"),
206 ctx.mockVpp.MockReply(msgs...)
207 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
209 reqCtx := ctx.ch.SendMultiRequest(&tap.SwInterfaceTapDump{})
212 msg := &tap.SwInterfaceTapDetails{}
213 stop, err := reqCtx.ReceiveReply(msg)
215 break // break out of the loop
217 Expect(err).ShouldNot(HaveOccurred())
220 Expect(cnt).To(BeEquivalentTo(10))
223 func TestMultiRequestReplySwInterfaceMemifDump(t *testing.T) {
225 defer ctx.teardownTest()
228 msgs := []api.Message{}
229 for i := 1; i <= 10; i++ {
230 msgs = append(msgs, &memif.MemifDetails{
231 SwIfIndex: uint32(i),
234 ctx.mockVpp.MockReply(msgs...)
235 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
237 reqCtx := ctx.ch.SendMultiRequest(&memif.MemifDump{})
240 msg := &memif.MemifDetails{}
241 stop, err := reqCtx.ReceiveReply(msg)
243 break // break out of the loop
245 Expect(err).ShouldNot(HaveOccurred())
248 Expect(cnt).To(BeEquivalentTo(10))
251 func TestNotifications(t *testing.T) {
253 defer ctx.teardownTest()
255 // subscribe for notification
256 notifChan := make(chan api.Message, 1)
257 subs, err := ctx.ch.SubscribeNotification(notifChan, interfaces.NewSwInterfaceSetFlags)
258 Expect(err).ShouldNot(HaveOccurred())
260 // mock the notification and force its delivery
261 ctx.mockVpp.MockReply(&interfaces.SwInterfaceSetFlags{
265 ctx.mockVpp.SendMsg(0, []byte(""))
267 // receive the notification
268 var notif *interfaces.SwInterfaceSetFlags
269 Eventually(func() *interfaces.SwInterfaceSetFlags {
271 case n := <-notifChan:
272 notif = n.(*interfaces.SwInterfaceSetFlags)
277 }).ShouldNot(BeNil())
279 // verify the received notifications
280 Expect(notif.SwIfIndex).To(BeEquivalentTo(3), "Incorrect SwIfIndex value for SwInterfaceSetFlags")
281 Expect(notif.AdminUpDown).To(BeEquivalentTo(1), "Incorrect AdminUpDown value for SwInterfaceSetFlags")
283 ctx.ch.UnsubscribeNotification(subs)
286 func TestNotificationEvent(t *testing.T) {
288 defer ctx.teardownTest()
290 // subscribe for notification
291 notifChan := make(chan api.Message, 1)
292 subs, err := ctx.ch.SubscribeNotification(notifChan, interfaces.NewSwInterfaceEvent)
293 Expect(err).ShouldNot(HaveOccurred())
295 // mock the notification and force its delivery
296 ctx.mockVpp.MockReply(&interfaces.SwInterfaceEvent{
300 ctx.mockVpp.SendMsg(0, []byte(""))
302 // receive the notification
303 var notif *interfaces.SwInterfaceEvent
304 Eventually(func() *interfaces.SwInterfaceEvent {
306 case n := <-notifChan:
307 notif = n.(*interfaces.SwInterfaceEvent)
312 }).ShouldNot(BeNil())
314 // verify the received notifications
315 Expect(notif.SwIfIndex).To(BeEquivalentTo(2), "Incorrect SwIfIndex value for SwInterfaceSetFlags")
316 Expect(notif.LinkUpDown).To(BeEquivalentTo(1), "Incorrect LinkUpDown value for SwInterfaceSetFlags")
318 ctx.ch.UnsubscribeNotification(subs)
321 func TestCheckMessageCompatibility(t *testing.T) {
323 defer ctx.teardownTest()
325 err := ctx.ch.CheckMessageCompatibility(&interfaces.SwInterfaceSetFlags{})
326 Expect(err).ShouldNot(HaveOccurred())
328 func TestSetReplyTimeout(t *testing.T) {
330 defer ctx.teardownTest()
332 ctx.ch.SetReplyTimeout(time.Millisecond)
334 // first one request should work
335 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
336 err := ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
337 Expect(err).ShouldNot(HaveOccurred())
339 // no other reply ready - expect timeout
340 err = ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
341 Expect(err).Should(HaveOccurred())
342 Expect(err.Error()).To(ContainSubstring("timeout"))
345 func TestSetReplyTimeoutMultiRequest(t *testing.T) {
347 defer ctx.teardownTest()
349 ctx.ch.SetReplyTimeout(time.Millisecond)
351 msgs := []api.Message{}
352 for i := 1; i <= 3; i++ {
353 msgs = append(msgs, &interfaces.SwInterfaceDetails{
354 SwIfIndex: uint32(i),
355 InterfaceName: []byte("if-name-test"),
358 ctx.mockVpp.MockReply(msgs...)
359 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
362 sendMultiRequest := func() error {
363 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
365 msg := &interfaces.SwInterfaceDetails{}
366 stop, err := reqCtx.ReceiveReply(msg)
368 break // break out of the loop
378 // first one request should work
379 err := sendMultiRequest()
380 Expect(err).ShouldNot(HaveOccurred())
382 // no other reply ready - expect timeout
383 err = sendMultiRequest()
384 Expect(err).Should(HaveOccurred())
385 Expect(err.Error()).To(ContainSubstring("timeout"))
387 Expect(cnt).To(BeEquivalentTo(3))
390 func TestReceiveReplyNegative(t *testing.T) {
392 defer ctx.teardownTest()
395 reqCtx1 := &api.RequestCtx{}
396 err := reqCtx1.ReceiveReply(&vpe.ControlPingReply{})
397 Expect(err).Should(HaveOccurred())
398 Expect(err.Error()).To(ContainSubstring("invalid request context"))
401 reqCtx2 := &api.MultiRequestCtx{}
402 _, err = reqCtx2.ReceiveReply(&vpe.ControlPingReply{})
403 Expect(err).Should(HaveOccurred())
404 Expect(err.Error()).To(ContainSubstring("invalid request context"))
407 reqCtx3 := &api.RequestCtx{}
408 err = reqCtx3.ReceiveReply(nil)
409 Expect(err).Should(HaveOccurred())
410 Expect(err.Error()).To(ContainSubstring("invalid request context"))
413 func TestMultiRequestDouble(t *testing.T) {
415 defer ctx.teardownTest()
418 msgs := []mock.MsgWithContext{}
419 for i := 1; i <= 3; i++ {
420 msgs = append(msgs, mock.MsgWithContext{
421 Msg: &interfaces.SwInterfaceDetails{
422 SwIfIndex: uint32(i),
423 InterfaceName: []byte("if-name-test"),
429 msgs = append(msgs, mock.MsgWithContext{Msg: &vpe.ControlPingReply{}, Multipart: true, SeqNum: 1})
431 for i := 1; i <= 3; i++ {
434 Msg: &interfaces.SwInterfaceDetails{
435 SwIfIndex: uint32(i),
436 InterfaceName: []byte("if-name-test"),
442 msgs = append(msgs, mock.MsgWithContext{Msg: &vpe.ControlPingReply{}, Multipart: true, SeqNum: 2})
444 ctx.mockVpp.MockReplyWithContext(msgs...)
447 var sendMultiRequest = func() error {
448 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
450 msg := &interfaces.SwInterfaceDetails{}
451 stop, err := reqCtx.ReceiveReply(msg)
453 break // break out of the loop
463 err := sendMultiRequest()
464 Expect(err).ShouldNot(HaveOccurred())
466 err = sendMultiRequest()
467 Expect(err).ShouldNot(HaveOccurred())
469 Expect(cnt).To(BeEquivalentTo(6))
472 func TestReceiveReplyAfterTimeout(t *testing.T) {
474 defer ctx.teardownTest()
476 ctx.ch.SetReplyTimeout(time.Millisecond)
478 // first one request should work
479 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &vpe.ControlPingReply{}, SeqNum: 1})
480 err := ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
481 Expect(err).ShouldNot(HaveOccurred())
483 err = ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
484 Expect(err).Should(HaveOccurred())
485 Expect(err.Error()).To(ContainSubstring("timeout"))
487 ctx.mockVpp.MockReplyWithContext(
488 // simulating late reply
489 mock.MsgWithContext{Msg: &vpe.ControlPingReply{}, SeqNum: 2},
490 // normal reply for next request
491 mock.MsgWithContext{Msg: &tap.TapConnectReply{}, SeqNum: 3})
493 req := &tap.TapConnect{
494 TapName: []byte("test-tap-name"),
497 reply := &tap.TapConnectReply{}
500 err = ctx.ch.SendRequest(req).ReceiveReply(reply)
501 Expect(err).ShouldNot(HaveOccurred())
504 func TestReceiveReplyAfterTimeoutMultiRequest(t *testing.T) {
506 TODO: fix mock adapter
507 This test will fail because mock adapter will stop sending replies
508 when it encounters control_ping_reply from multi request,
509 thus never sending reply for next request
514 defer ctx.teardownTest()
516 ctx.ch.SetReplyTimeout(time.Millisecond * 100)
518 // first one request should work
519 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &vpe.ControlPingReply{}, SeqNum: 1})
520 err := ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
521 Expect(err).ShouldNot(HaveOccurred())
524 var sendMultiRequest = func() error {
525 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
527 msg := &interfaces.SwInterfaceDetails{}
528 stop, err := reqCtx.ReceiveReply(msg)
530 break // break out of the loop
540 err = sendMultiRequest()
541 Expect(err).Should(HaveOccurred())
542 Expect(err.Error()).To(ContainSubstring("timeout"))
543 Expect(cnt).To(BeEquivalentTo(0))
545 // simulating late replies
546 msgs := []mock.MsgWithContext{}
547 for i := 1; i <= 3; i++ {
548 msgs = append(msgs, mock.MsgWithContext{
549 Msg: &interfaces.SwInterfaceDetails{
550 SwIfIndex: uint32(i),
551 InterfaceName: []byte("if-name-test"),
557 msgs = append(msgs, mock.MsgWithContext{Msg: &vpe.ControlPingReply{}, Multipart: true, SeqNum: 2})
558 ctx.mockVpp.MockReplyWithContext(msgs...)
560 // normal reply for next request
561 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &tap.TapConnectReply{}, SeqNum: 3})
563 req := &tap.TapConnect{
564 TapName: []byte("test-tap-name"),
567 reply := &tap.TapConnectReply{}
570 err = ctx.ch.SendRequest(req).ReceiveReply(reply)
571 Expect(err).ShouldNot(HaveOccurred())
574 func TestInvalidMessageID(t *testing.T) {
576 defer ctx.teardownTest()
578 // first one request should work
579 ctx.mockVpp.MockReply(&vpe.ShowVersionReply{})
580 err := ctx.ch.SendRequest(&vpe.ShowVersion{}).ReceiveReply(&vpe.ShowVersionReply{})
581 Expect(err).ShouldNot(HaveOccurred())
583 // second should fail with error invalid message ID
584 ctx.mockVpp.MockReply(&vpe.ShowVersionReply{})
585 err = ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
586 Expect(err).Should(HaveOccurred())
587 Expect(err.Error()).To(ContainSubstring("invalid message ID"))