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"
26 "git.fd.io/govpp.git/api"
27 . "github.com/onsi/gomega"
31 mockVpp *mock.VppAdapter
36 func setupTest(t *testing.T) *testCtx {
40 mockVpp: &mock.VppAdapter{},
44 ctx.conn, err = Connect(ctx.mockVpp)
45 Expect(err).ShouldNot(HaveOccurred())
47 ctx.ch, err = ctx.conn.NewAPIChannel()
48 Expect(err).ShouldNot(HaveOccurred())
53 func (ctx *testCtx) teardownTest() {
58 func TestRequestReplyTapConnect(t *testing.T) {
60 defer ctx.teardownTest()
62 ctx.mockVpp.MockReply(&tap.TapConnectReply{
66 request := &tap.TapConnect{
67 TapName: []byte("test-tap-name"),
70 reply := &tap.TapConnectReply{}
72 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
73 Expect(err).ShouldNot(HaveOccurred())
74 Expect(reply.Retval).To(BeEquivalentTo(0), "Incorrect retval value for TapConnectReply")
75 Expect(reply.SwIfIndex).To(BeEquivalentTo(1), "Incorrect SwIfIndex value for TapConnectReply")
78 func TestRequestReplyTapModify(t *testing.T) {
80 defer ctx.teardownTest()
82 ctx.mockVpp.MockReply(&tap.TapModifyReply{
85 request := &tap.TapModify{
86 TapName: []byte("test-tap-modify"),
90 reply := &tap.TapModifyReply{}
92 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
93 Expect(err).ShouldNot(HaveOccurred())
94 Expect(reply.Retval).To(BeEquivalentTo(0), "Incorrect retval value for TapModifyReply")
95 Expect(reply.SwIfIndex).To(BeEquivalentTo(2), "Incorrect SwIfIndex value for TapModifyReply")
98 func TestRequestReplyTapDelete(t *testing.T) {
100 defer ctx.teardownTest()
102 ctx.mockVpp.MockReply(&tap.TapDeleteReply{})
103 request := &tap.TapDelete{
106 reply := &tap.TapDeleteReply{}
108 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
109 Expect(err).ShouldNot(HaveOccurred())
110 Expect(reply.Retval).To(BeEquivalentTo(0), "Incorrect retval value for TapDeleteReply")
113 func TestRequestReplySwInterfaceTapDump(t *testing.T) {
115 defer ctx.teardownTest()
117 byteName := []byte("dev-name-test")
118 ctx.mockVpp.MockReply(&tap.SwInterfaceTapDetails{
122 request := &tap.SwInterfaceTapDump{}
123 reply := &tap.SwInterfaceTapDetails{}
125 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
126 Expect(err).ShouldNot(HaveOccurred())
127 Expect(reply.SwIfIndex).To(BeEquivalentTo(25), "Incorrect SwIfIndex value for SwInterfaceTapDetails")
128 Expect(reply.DevName).ToNot(BeNil(), "Incorrect DevName value for SwInterfaceTapDetails")
131 func TestRequestReplyMemifCreate(t *testing.T) {
133 defer ctx.teardownTest()
135 ctx.mockVpp.MockReply(&memif.MemifCreateReply{
138 request := &memif.MemifCreate{
144 reply := &memif.MemifCreateReply{}
146 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
147 Expect(err).ShouldNot(HaveOccurred())
148 Expect(reply.Retval).To(BeEquivalentTo(0), "Incorrect Retval value for MemifCreate")
149 Expect(reply.SwIfIndex).To(BeEquivalentTo(4), "Incorrect SwIfIndex value for MemifCreate")
152 func TestRequestReplyMemifDelete(t *testing.T) {
154 defer ctx.teardownTest()
156 ctx.mockVpp.MockReply(&memif.MemifDeleteReply{})
157 request := &memif.MemifDelete{
160 reply := &memif.MemifDeleteReply{}
162 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
163 Expect(err).ShouldNot(HaveOccurred())
164 Expect(reply.Retval).To(BeEquivalentTo(0), "Incorrect Retval value for MemifDelete")
167 func TestRequestReplyMemifDetails(t *testing.T) {
169 defer ctx.teardownTest()
171 ctx.mockVpp.MockReply(&memif.MemifDetails{
173 IfName: []byte("memif-name"),
176 request := &memif.MemifDump{}
177 reply := &memif.MemifDetails{}
179 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
180 Expect(err).ShouldNot(HaveOccurred())
181 Expect(reply.SwIfIndex).To(BeEquivalentTo(25), "Incorrect SwIfIndex value for MemifDetails")
182 Expect(reply.IfName).ToNot(BeEmpty(), "MemifDetails IfName is empty byte array")
183 Expect(reply.Role).To(BeEquivalentTo(0), "Incorrect Role value for MemifDetails")
186 func TestMultiRequestReplySwInterfaceTapDump(t *testing.T) {
188 defer ctx.teardownTest()
191 var msgs []api.Message
192 for i := 1; i <= 10; i++ {
193 msgs = append(msgs, &tap.SwInterfaceTapDetails{
194 SwIfIndex: uint32(i),
195 DevName: []byte("dev-name-test"),
198 ctx.mockVpp.MockReply(msgs...)
199 ctx.mockVpp.MockReply(&ControlPingReply{})
201 reqCtx := ctx.ch.SendMultiRequest(&tap.SwInterfaceTapDump{})
204 msg := &tap.SwInterfaceTapDetails{}
205 stop, err := reqCtx.ReceiveReply(msg)
207 break // break out of the loop
209 Expect(err).ShouldNot(HaveOccurred())
212 Expect(cnt).To(BeEquivalentTo(10))
215 func TestMultiRequestReplySwInterfaceMemifDump(t *testing.T) {
217 defer ctx.teardownTest()
220 var msgs []api.Message
221 for i := 1; i <= 10; i++ {
222 msgs = append(msgs, &memif.MemifDetails{
223 SwIfIndex: uint32(i),
226 ctx.mockVpp.MockReply(msgs...)
227 ctx.mockVpp.MockReply(&ControlPingReply{})
229 reqCtx := ctx.ch.SendMultiRequest(&memif.MemifDump{})
232 msg := &memif.MemifDetails{}
233 stop, err := reqCtx.ReceiveReply(msg)
235 break // break out of the loop
237 Expect(err).ShouldNot(HaveOccurred())
240 Expect(cnt).To(BeEquivalentTo(10))
243 func TestNotifications(t *testing.T) {
245 defer ctx.teardownTest()
247 // subscribe for notification
248 notifChan := make(chan api.Message, 1)
249 subs, err := ctx.ch.SubscribeNotification(notifChan, interfaces.NewSwInterfaceSetFlags)
250 Expect(err).ShouldNot(HaveOccurred())
252 // mock the notification and force its delivery
253 ctx.mockVpp.MockReply(&interfaces.SwInterfaceSetFlags{
257 ctx.mockVpp.SendMsg(0, []byte(""))
259 // receive the notification
260 var notif *interfaces.SwInterfaceSetFlags
261 Eventually(func() *interfaces.SwInterfaceSetFlags {
263 case n := <-notifChan:
264 notif = n.(*interfaces.SwInterfaceSetFlags)
269 }).ShouldNot(BeNil())
271 // verify the received notifications
272 Expect(notif.SwIfIndex).To(BeEquivalentTo(3), "Incorrect SwIfIndex value for SwInterfaceSetFlags")
273 Expect(notif.AdminUpDown).To(BeEquivalentTo(1), "Incorrect AdminUpDown value for SwInterfaceSetFlags")
275 ctx.ch.UnsubscribeNotification(subs)
278 func TestNotificationEvent(t *testing.T) {
280 defer ctx.teardownTest()
282 // subscribe for notification
283 notifChan := make(chan api.Message, 1)
284 subs, err := ctx.ch.SubscribeNotification(notifChan, interfaces.NewSwInterfaceEvent)
285 Expect(err).ShouldNot(HaveOccurred())
287 // mock the notification and force its delivery
288 ctx.mockVpp.MockReply(&interfaces.SwInterfaceEvent{
292 ctx.mockVpp.SendMsg(0, []byte(""))
294 // receive the notification
295 var notif *interfaces.SwInterfaceEvent
296 Eventually(func() *interfaces.SwInterfaceEvent {
298 case n := <-notifChan:
299 notif = n.(*interfaces.SwInterfaceEvent)
304 }).ShouldNot(BeNil())
306 // verify the received notifications
307 Expect(notif.SwIfIndex).To(BeEquivalentTo(2), "Incorrect SwIfIndex value for SwInterfaceSetFlags")
308 Expect(notif.LinkUpDown).To(BeEquivalentTo(1), "Incorrect LinkUpDown value for SwInterfaceSetFlags")
310 ctx.ch.UnsubscribeNotification(subs)
313 /*func TestCheckMessageCompatibility(t *testing.T) {
315 defer ctx.teardownTest()
317 err := ctx.ch.CheckMessageCompatibility(&interfaces.SwInterfaceSetFlags{})
318 Expect(err).ShouldNot(HaveOccurred())
321 func TestSetReplyTimeout(t *testing.T) {
323 defer ctx.teardownTest()
325 ctx.ch.SetReplyTimeout(time.Millisecond)
327 // first one request should work
328 ctx.mockVpp.MockReply(&ControlPingReply{})
329 err := ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
330 Expect(err).ShouldNot(HaveOccurred())
332 // no other reply ready - expect timeout
333 err = ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
334 Expect(err).Should(HaveOccurred())
335 Expect(err.Error()).To(ContainSubstring("timeout"))
338 func TestSetReplyTimeoutMultiRequest(t *testing.T) {
340 defer ctx.teardownTest()
342 ctx.ch.SetReplyTimeout(time.Millisecond)
344 var msgs []api.Message
345 for i := 1; i <= 3; i++ {
346 msgs = append(msgs, &interfaces.SwInterfaceDetails{
347 SwIfIndex: uint32(i),
348 InterfaceName: []byte("if-name-test"),
351 ctx.mockVpp.MockReply(msgs...)
352 ctx.mockVpp.MockReply(&ControlPingReply{})
355 sendMultiRequest := func() error {
356 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
358 msg := &interfaces.SwInterfaceDetails{}
359 stop, err := reqCtx.ReceiveReply(msg)
361 break // break out of the loop
371 // first one request should work
372 err := sendMultiRequest()
373 Expect(err).ShouldNot(HaveOccurred())
375 // no other reply ready - expect timeout
376 err = sendMultiRequest()
377 Expect(err).Should(HaveOccurred())
378 Expect(err.Error()).To(ContainSubstring("timeout"))
380 Expect(cnt).To(BeEquivalentTo(3))
383 func TestReceiveReplyNegative(t *testing.T) {
385 defer ctx.teardownTest()
388 reqCtx1 := &requestCtx{}
389 err := reqCtx1.ReceiveReply(&ControlPingReply{})
390 Expect(err).Should(HaveOccurred())
391 Expect(err.Error()).To(ContainSubstring("invalid request context"))
394 reqCtx2 := &multiRequestCtx{}
395 _, err = reqCtx2.ReceiveReply(&ControlPingReply{})
396 Expect(err).Should(HaveOccurred())
397 Expect(err.Error()).To(ContainSubstring("invalid request context"))
400 reqCtx3 := &requestCtx{}
401 err = reqCtx3.ReceiveReply(nil)
402 Expect(err).Should(HaveOccurred())
403 Expect(err.Error()).To(ContainSubstring("invalid request context"))
406 func TestMultiRequestDouble(t *testing.T) {
408 defer ctx.teardownTest()
411 var msgs []mock.MsgWithContext
412 for i := 1; i <= 3; i++ {
413 msgs = append(msgs, mock.MsgWithContext{
414 Msg: &interfaces.SwInterfaceDetails{
415 SwIfIndex: uint32(i),
416 InterfaceName: []byte("if-name-test"),
422 msgs = append(msgs, mock.MsgWithContext{Msg: &ControlPingReply{}, Multipart: true, SeqNum: 1})
424 for i := 1; i <= 3; i++ {
427 Msg: &interfaces.SwInterfaceDetails{
428 SwIfIndex: uint32(i),
429 InterfaceName: []byte("if-name-test"),
435 msgs = append(msgs, mock.MsgWithContext{Msg: &ControlPingReply{}, Multipart: true, SeqNum: 2})
437 ctx.mockVpp.MockReplyWithContext(msgs...)
440 var sendMultiRequest = func() error {
441 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
443 msg := &interfaces.SwInterfaceDetails{}
444 stop, err := reqCtx.ReceiveReply(msg)
446 break // break out of the loop
456 err := sendMultiRequest()
457 Expect(err).ShouldNot(HaveOccurred())
459 err = sendMultiRequest()
460 Expect(err).ShouldNot(HaveOccurred())
462 Expect(cnt).To(BeEquivalentTo(6))
465 func TestReceiveReplyAfterTimeout(t *testing.T) {
467 defer ctx.teardownTest()
469 ctx.ch.SetReplyTimeout(time.Millisecond)
471 // first one request should work
472 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &ControlPingReply{}, SeqNum: 1})
473 err := ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
474 Expect(err).ShouldNot(HaveOccurred())
476 err = ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
477 Expect(err).Should(HaveOccurred())
478 Expect(err.Error()).To(ContainSubstring("timeout"))
480 ctx.mockVpp.MockReplyWithContext(
481 // simulating late reply
482 mock.MsgWithContext{Msg: &ControlPingReply{}, SeqNum: 2},
483 // normal reply for next request
484 mock.MsgWithContext{Msg: &tap.TapConnectReply{}, SeqNum: 3})
486 req := &tap.TapConnect{
487 TapName: []byte("test-tap-name"),
490 reply := &tap.TapConnectReply{}
493 err = ctx.ch.SendRequest(req).ReceiveReply(reply)
494 Expect(err).ShouldNot(HaveOccurred())
497 func TestReceiveReplyAfterTimeoutMultiRequest(t *testing.T) {
499 TODO: fix mock adapter
500 This test will fail because mock adapter will stop sending replies
501 when it encounters control_ping_reply from multi request,
502 thus never sending reply for next request
507 defer ctx.teardownTest()
509 ctx.ch.SetReplyTimeout(time.Millisecond * 100)
511 // first one request should work
512 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &ControlPingReply{}, SeqNum: 1})
513 err := ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
514 Expect(err).ShouldNot(HaveOccurred())
517 var sendMultiRequest = func() error {
518 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
520 msg := &interfaces.SwInterfaceDetails{}
521 stop, err := reqCtx.ReceiveReply(msg)
523 break // break out of the loop
533 err = sendMultiRequest()
534 Expect(err).Should(HaveOccurred())
535 Expect(err.Error()).To(ContainSubstring("timeout"))
536 Expect(cnt).To(BeEquivalentTo(0))
538 // simulating late replies
539 var msgs []mock.MsgWithContext
540 for i := 1; i <= 3; i++ {
541 msgs = append(msgs, mock.MsgWithContext{
542 Msg: &interfaces.SwInterfaceDetails{
543 SwIfIndex: uint32(i),
544 InterfaceName: []byte("if-name-test"),
550 msgs = append(msgs, mock.MsgWithContext{Msg: &ControlPingReply{}, Multipart: true, SeqNum: 2})
551 ctx.mockVpp.MockReplyWithContext(msgs...)
553 // normal reply for next request
554 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &tap.TapConnectReply{}, SeqNum: 3})
556 req := &tap.TapConnect{
557 TapName: []byte("test-tap-name"),
560 reply := &tap.TapConnectReply{}
563 err = ctx.ch.SendRequest(req).ReceiveReply(reply)
564 Expect(err).ShouldNot(HaveOccurred())
567 /*func TestInvalidMessageID(t *testing.T) {
569 defer ctx.teardownTest()
571 // first one request should work
572 ctx.mockVpp.MockReply(&vpe.ShowVersionReply{})
573 err := ctx.ch.SendRequest(&vpe.ShowVersion{}).ReceiveReply(&vpe.ShowVersionReply{})
574 Expect(err).ShouldNot(HaveOccurred())
576 // second should fail with error invalid message ID
577 ctx.mockVpp.MockReply(&vpe.ShowVersionReply{})
578 err = ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
579 Expect(err).Should(HaveOccurred())
580 Expect(err.Error()).To(ContainSubstring("invalid message ID"))