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/examples/bin_api/interfaces"
23 "git.fd.io/govpp.git/examples/bin_api/memif"
24 "git.fd.io/govpp.git/examples/bin_api/tap"
25 "git.fd.io/govpp.git/examples/bin_api/vpe"
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.NewVppAdapter(),
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()
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(0),
77 "Incorrect Retval value for TapConnectReply")
78 Expect(reply.SwIfIndex).To(BeEquivalentTo(1),
79 "Incorrect SwIfIndex value for TapConnectReply")
82 func TestRequestReplyTapModify(t *testing.T) {
84 defer ctx.teardownTest()
87 ctx.mockVpp.MockReply(&tap.TapModifyReply{
91 request := &tap.TapModify{
92 TapName: []byte("test-tap-modify"),
96 reply := &tap.TapModifyReply{}
98 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
99 Expect(err).ShouldNot(HaveOccurred())
100 Expect(reply.Retval).To(BeEquivalentTo(0),
101 "Incorrect Retval value for TapModifyReply")
102 Expect(reply.SwIfIndex).To(BeEquivalentTo(2),
103 "Incorrect SwIfIndex value for TapModifyReply")
106 func TestRequestReplyTapDelete(t *testing.T) {
108 defer ctx.teardownTest()
111 ctx.mockVpp.MockReply(&tap.TapDeleteReply{})
113 request := &tap.TapDelete{
116 reply := &tap.TapDeleteReply{}
118 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
119 Expect(err).ShouldNot(HaveOccurred())
120 Expect(reply.Retval).To(BeEquivalentTo(0),
121 "Incorrect Retval value for TapDeleteReply")
124 func TestRequestReplySwInterfaceTapDump(t *testing.T) {
126 defer ctx.teardownTest()
129 byteName := []byte("dev-name-test")
130 ctx.mockVpp.MockReply(&tap.SwInterfaceTapDetails{
135 request := &tap.SwInterfaceTapDump{}
136 reply := &tap.SwInterfaceTapDetails{}
138 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
139 Expect(err).ShouldNot(HaveOccurred())
140 Expect(reply.SwIfIndex).To(BeEquivalentTo(25),
141 "Incorrect SwIfIndex value for SwInterfaceTapDetails")
142 Expect(reply.DevName).ToNot(BeEquivalentTo(byteName),
143 "Incorrect DevName value for SwInterfaceTapDetails")
146 func TestRequestReplyMemifCreate(t *testing.T) {
148 defer ctx.teardownTest()
151 ctx.mockVpp.MockReply(&memif.MemifCreateReply{
155 request := &memif.MemifCreate{
161 reply := &memif.MemifCreateReply{}
163 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
164 Expect(err).ShouldNot(HaveOccurred())
165 Expect(reply.Retval).To(BeEquivalentTo(0),
166 "Incorrect Retval value for MemifCreate")
167 Expect(reply.SwIfIndex).To(BeEquivalentTo(4),
168 "Incorrect SwIfIndex value for MemifCreate")
171 func TestRequestReplyMemifDelete(t *testing.T) {
173 defer ctx.teardownTest()
176 ctx.mockVpp.MockReply(&memif.MemifDeleteReply{})
178 request := &memif.MemifDelete{
181 reply := &memif.MemifDeleteReply{}
183 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
184 Expect(err).ShouldNot(HaveOccurred())
187 func TestRequestReplyMemifDetails(t *testing.T) {
189 defer ctx.teardownTest()
192 ctx.mockVpp.MockReply(&memif.MemifDetails{
194 IfName: []byte("memif-name"),
198 request := &memif.MemifDump{}
199 reply := &memif.MemifDetails{}
201 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
202 Expect(err).ShouldNot(HaveOccurred())
203 Expect(reply.SwIfIndex).To(BeEquivalentTo(25),
204 "Incorrect SwIfIndex value for MemifDetails")
205 Expect(reply.IfName).ToNot(BeEmpty(),
206 "MemifDetails IfName is empty byte array")
207 Expect(reply.Role).To(BeEquivalentTo(0),
208 "Incorrect Role value for MemifDetails")
211 func TestMultiRequestReplySwInterfaceTapDump(t *testing.T) {
213 defer ctx.teardownTest()
216 var msgs []api.Message
217 for i := 1; i <= 10; i++ {
218 msgs = append(msgs, &tap.SwInterfaceTapDetails{
219 SwIfIndex: uint32(i),
220 DevName: []byte("dev-name-test"),
223 ctx.mockVpp.MockReply(msgs...)
224 ctx.mockVpp.MockReply(&ControlPingReply{})
226 reqCtx := ctx.ch.SendMultiRequest(&tap.SwInterfaceTapDump{})
229 msg := &tap.SwInterfaceTapDetails{}
230 stop, err := reqCtx.ReceiveReply(msg)
234 Expect(err).ShouldNot(HaveOccurred())
237 Expect(cnt).To(BeEquivalentTo(10))
240 func TestMultiRequestReplySwInterfaceMemifDump(t *testing.T) {
242 defer ctx.teardownTest()
245 var msgs []api.Message
246 for i := 1; i <= 10; i++ {
247 msgs = append(msgs, &memif.MemifDetails{
248 SwIfIndex: uint32(i),
251 ctx.mockVpp.MockReply(msgs...)
252 ctx.mockVpp.MockReply(&ControlPingReply{})
254 reqCtx := ctx.ch.SendMultiRequest(&memif.MemifDump{})
257 msg := &memif.MemifDetails{}
258 stop, err := reqCtx.ReceiveReply(msg)
262 Expect(err).ShouldNot(HaveOccurred())
265 Expect(cnt).To(BeEquivalentTo(10))
268 func TestNotificationEvent(t *testing.T) {
270 defer ctx.teardownTest()
272 // subscribe for notification
273 notifChan := make(chan api.Message, 1)
274 sub, err := ctx.ch.SubscribeNotification(notifChan, &interfaces.SwInterfaceEvent{})
275 Expect(err).ShouldNot(HaveOccurred())
277 // mock event and force its delivery
278 ctx.mockVpp.MockReply(&interfaces.SwInterfaceEvent{
282 ctx.mockVpp.SendMsg(0, []byte(""))
284 // receive the notification
285 var notif *interfaces.SwInterfaceEvent
286 Eventually(func() *interfaces.SwInterfaceEvent {
288 case n := <-notifChan:
289 notif = n.(*interfaces.SwInterfaceEvent)
294 }).ShouldNot(BeNil())
296 // verify the received notifications
297 Expect(notif.SwIfIndex).To(BeEquivalentTo(2), "Incorrect SwIfIndex value for SwInterfaceSetFlags")
298 Expect(notif.LinkUpDown).To(BeEquivalentTo(1), "Incorrect LinkUpDown value for SwInterfaceSetFlags")
300 err = sub.Unsubscribe()
301 Expect(err).ShouldNot(HaveOccurred())
304 func TestSetReplyTimeout(t *testing.T) {
306 defer ctx.teardownTest()
308 ctx.ch.SetReplyTimeout(time.Millisecond)
311 ctx.mockVpp.MockReply(&ControlPingReply{})
313 // first one request should work
314 err := ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
315 Expect(err).ShouldNot(HaveOccurred())
317 // no other reply ready - expect timeout
318 err = ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
319 Expect(err).Should(HaveOccurred())
320 Expect(err.Error()).To(ContainSubstring("timeout"))
323 func TestSetReplyTimeoutMultiRequest(t *testing.T) {
325 defer ctx.teardownTest()
327 ctx.ch.SetReplyTimeout(time.Millisecond * 100)
330 ctx.mockVpp.MockReply(
331 &interfaces.SwInterfaceDetails{
333 InterfaceName: []byte("if-name-test"),
335 &interfaces.SwInterfaceDetails{
337 InterfaceName: []byte("if-name-test"),
339 &interfaces.SwInterfaceDetails{
341 InterfaceName: []byte("if-name-test"),
344 ctx.mockVpp.MockReply(&ControlPingReply{})
347 sendMultiRequest := func() error {
348 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
350 msg := &interfaces.SwInterfaceDetails{}
351 stop, err := reqCtx.ReceiveReply(msg)
363 // first one request should work
364 err := sendMultiRequest()
365 Expect(err).ShouldNot(HaveOccurred())
367 // no other reply ready - expect timeout
368 err = sendMultiRequest()
369 Expect(err).Should(HaveOccurred())
370 Expect(err.Error()).To(ContainSubstring("timeout"))
372 Expect(cnt).To(BeEquivalentTo(3))
375 func TestReceiveReplyNegative(t *testing.T) {
377 defer ctx.teardownTest()
380 reqCtx1 := &requestCtx{}
381 err := reqCtx1.ReceiveReply(&ControlPingReply{})
382 Expect(err).Should(HaveOccurred())
383 Expect(err.Error()).To(ContainSubstring("invalid request context"))
386 reqCtx2 := &multiRequestCtx{}
387 _, err = reqCtx2.ReceiveReply(&ControlPingReply{})
388 Expect(err).Should(HaveOccurred())
389 Expect(err.Error()).To(ContainSubstring("invalid request context"))
392 reqCtx3 := &requestCtx{}
393 err = reqCtx3.ReceiveReply(nil)
394 Expect(err).Should(HaveOccurred())
395 Expect(err.Error()).To(ContainSubstring("invalid request context"))
398 func TestMultiRequestDouble(t *testing.T) {
400 defer ctx.teardownTest()
403 var msgs []mock.MsgWithContext
404 for i := 1; i <= 3; i++ {
405 msgs = append(msgs, mock.MsgWithContext{
406 Msg: &interfaces.SwInterfaceDetails{
407 SwIfIndex: uint32(i),
408 InterfaceName: []byte("if-name-test"),
414 msgs = append(msgs, mock.MsgWithContext{Msg: &ControlPingReply{}, Multipart: true, SeqNum: 1})
416 for i := 1; i <= 3; i++ {
419 Msg: &interfaces.SwInterfaceDetails{
420 SwIfIndex: uint32(i),
421 InterfaceName: []byte("if-name-test"),
427 msgs = append(msgs, mock.MsgWithContext{Msg: &ControlPingReply{}, Multipart: true, SeqNum: 2})
429 ctx.mockVpp.MockReplyWithContext(msgs...)
432 var sendMultiRequest = func() error {
433 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
435 msg := &interfaces.SwInterfaceDetails{}
436 stop, err := reqCtx.ReceiveReply(msg)
448 err := sendMultiRequest()
449 Expect(err).ShouldNot(HaveOccurred())
451 err = sendMultiRequest()
452 Expect(err).ShouldNot(HaveOccurred())
454 Expect(cnt).To(BeEquivalentTo(6))
457 func TestReceiveReplyAfterTimeout(t *testing.T) {
459 defer ctx.teardownTest()
461 ctx.ch.SetReplyTimeout(time.Millisecond)
464 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &ControlPingReply{}, SeqNum: 1})
465 // first one request should work
467 err := ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
468 Expect(err).ShouldNot(HaveOccurred())
470 err = ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
471 Expect(err).Should(HaveOccurred())
472 Expect(err.Error()).To(ContainSubstring("timeout"))
474 ctx.mockVpp.MockReplyWithContext(
475 // simulating late reply
477 Msg: &ControlPingReply{},
480 // normal reply for next request
482 Msg: &tap.TapConnectReply{},
487 req := &tap.TapConnect{
488 TapName: []byte("test-tap-name"),
491 reply := &tap.TapConnectReply{}
494 err = ctx.ch.SendRequest(req).ReceiveReply(reply)
495 Expect(err).ShouldNot(HaveOccurred())
498 func TestReceiveReplyAfterTimeoutMultiRequest(t *testing.T) {
500 TODO: fix mock adapter
501 This test will fail because mock adapter will stop sending replies
502 when it encounters control_ping_reply from multi request,
503 thus never sending reply for next request
508 defer ctx.teardownTest()
510 ctx.ch.SetReplyTimeout(time.Millisecond * 100)
513 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &ControlPingReply{}, SeqNum: 1})
515 // first one request should work
516 err := ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
517 Expect(err).ShouldNot(HaveOccurred())
520 var sendMultiRequest = func() error {
521 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
523 msg := &interfaces.SwInterfaceDetails{}
524 stop, err := reqCtx.ReceiveReply(msg)
536 err = sendMultiRequest()
537 Expect(err).Should(HaveOccurred())
538 Expect(err.Error()).To(ContainSubstring("timeout"))
539 Expect(cnt).To(BeEquivalentTo(0))
541 // simulating late replies
542 var msgs []mock.MsgWithContext
543 for i := 1; i <= 3; i++ {
544 msgs = append(msgs, mock.MsgWithContext{
545 Msg: &interfaces.SwInterfaceDetails{
546 SwIfIndex: uint32(i),
547 InterfaceName: []byte("if-name-test"),
553 msgs = append(msgs, mock.MsgWithContext{Msg: &ControlPingReply{}, Multipart: true, SeqNum: 2})
554 ctx.mockVpp.MockReplyWithContext(msgs...)
556 // normal reply for next request
557 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &tap.TapConnectReply{}, SeqNum: 3})
559 req := &tap.TapConnect{
560 TapName: []byte("test-tap-name"),
563 reply := &tap.TapConnectReply{}
566 err = ctx.ch.SendRequest(req).ReceiveReply(reply)
567 Expect(err).ShouldNot(HaveOccurred())
570 func TestInvalidMessageID(t *testing.T) {
572 defer ctx.teardownTest()
575 ctx.mockVpp.MockReply(&vpe.ShowVersionReply{})
576 ctx.mockVpp.MockReply(&vpe.ShowVersionReply{})
578 // first one request should work
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 err = ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
584 Expect(err).Should(HaveOccurred())
585 Expect(err.Error()).To(ContainSubstring("invalid message ID"))