aboutsummaryrefslogtreecommitdiffstats
path: root/core/channel_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'core/channel_test.go')
-rw-r--r--core/channel_test.go587
1 files changed, 587 insertions, 0 deletions
diff --git a/core/channel_test.go b/core/channel_test.go
new file mode 100644
index 0000000..d573f29
--- /dev/null
+++ b/core/channel_test.go
@@ -0,0 +1,587 @@
+// Copyright (c) 2017 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package core
+
+import (
+ "testing"
+ "time"
+
+ "git.fd.io/govpp.git/adapter/mock"
+ "git.fd.io/govpp.git/core/bin_api/vpe"
+ "git.fd.io/govpp.git/examples/bin_api/interfaces"
+ "git.fd.io/govpp.git/examples/bin_api/memif"
+ "git.fd.io/govpp.git/examples/bin_api/tap"
+
+ "git.fd.io/govpp.git/api"
+ . "github.com/onsi/gomega"
+)
+
+type testCtx struct {
+ mockVpp *mock.VppAdapter
+ conn *Connection
+ ch api.Channel
+}
+
+func setupTest(t *testing.T) *testCtx {
+ RegisterTestingT(t)
+
+ ctx := &testCtx{
+ mockVpp: &mock.VppAdapter{},
+ }
+
+ var err error
+ ctx.conn, err = Connect(ctx.mockVpp)
+ Expect(err).ShouldNot(HaveOccurred())
+
+ ctx.ch, err = ctx.conn.NewAPIChannel()
+ Expect(err).ShouldNot(HaveOccurred())
+
+ return ctx
+}
+
+func (ctx *testCtx) teardownTest() {
+ ctx.ch.Close()
+ ctx.conn.Disconnect()
+}
+
+func TestRequestReplyTapConnect(t *testing.T) {
+ ctx := setupTest(t)
+ defer ctx.teardownTest()
+
+ ctx.mockVpp.MockReply(&tap.TapConnectReply{
+ Retval: 10,
+ SwIfIndex: 1,
+ })
+ request := &tap.TapConnect{
+ TapName: []byte("test-tap-name"),
+ UseRandomMac: 1,
+ }
+ reply := &tap.TapConnectReply{}
+
+ err := ctx.ch.SendRequest(request).ReceiveReply(reply)
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(reply.Retval).To(BeEquivalentTo(10), "Incorrect retval value for TapConnectReply")
+ Expect(reply.SwIfIndex).To(BeEquivalentTo(1), "Incorrect SwIfIndex value for TapConnectReply")
+}
+
+func TestRequestReplyTapModify(t *testing.T) {
+ ctx := setupTest(t)
+ defer ctx.teardownTest()
+
+ ctx.mockVpp.MockReply(&tap.TapModifyReply{
+ Retval: 15,
+ SwIfIndex: 2,
+ })
+ request := &tap.TapModify{
+ TapName: []byte("test-tap-modify"),
+ UseRandomMac: 1,
+ CustomDevInstance: 1,
+ }
+ reply := &tap.TapModifyReply{}
+
+ err := ctx.ch.SendRequest(request).ReceiveReply(reply)
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(reply.Retval).To(BeEquivalentTo(15), "Incorrect retval value for TapModifyReply")
+ Expect(reply.SwIfIndex).To(BeEquivalentTo(2), "Incorrect SwIfIndex value for TapModifyReply")
+}
+
+func TestRequestReplyTapDelete(t *testing.T) {
+ ctx := setupTest(t)
+ defer ctx.teardownTest()
+
+ ctx.mockVpp.MockReply(&tap.TapDeleteReply{
+ Retval: 20,
+ })
+ request := &tap.TapDelete{
+ SwIfIndex: 3,
+ }
+ reply := &tap.TapDeleteReply{}
+
+ err := ctx.ch.SendRequest(request).ReceiveReply(reply)
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(reply.Retval).To(BeEquivalentTo(20), "Incorrect retval value for TapDeleteReply")
+}
+
+func TestRequestReplySwInterfaceTapDump(t *testing.T) {
+ ctx := setupTest(t)
+ defer ctx.teardownTest()
+
+ byteName := []byte("dev-name-test")
+ ctx.mockVpp.MockReply(&tap.SwInterfaceTapDetails{
+ SwIfIndex: 25,
+ DevName: byteName,
+ })
+ request := &tap.SwInterfaceTapDump{}
+ reply := &tap.SwInterfaceTapDetails{}
+
+ err := ctx.ch.SendRequest(request).ReceiveReply(reply)
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(reply.SwIfIndex).To(BeEquivalentTo(25), "Incorrect SwIfIndex value for SwInterfaceTapDetails")
+ Expect(reply.DevName).ToNot(BeNil(), "Incorrect DevName value for SwInterfaceTapDetails")
+}
+
+func TestRequestReplyMemifCreate(t *testing.T) {
+ ctx := setupTest(t)
+ defer ctx.teardownTest()
+
+ ctx.mockVpp.MockReply(&memif.MemifCreateReply{
+ Retval: 22,
+ SwIfIndex: 4,
+ })
+ request := &memif.MemifCreate{
+ Role: 10,
+ ID: 12,
+ RingSize: 8000,
+ BufferSize: 50,
+ }
+ reply := &memif.MemifCreateReply{}
+
+ err := ctx.ch.SendRequest(request).ReceiveReply(reply)
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(reply.Retval).To(BeEquivalentTo(22), "Incorrect Retval value for MemifCreate")
+ Expect(reply.SwIfIndex).To(BeEquivalentTo(4), "Incorrect SwIfIndex value for MemifCreate")
+}
+
+func TestRequestReplyMemifDelete(t *testing.T) {
+ ctx := setupTest(t)
+ defer ctx.teardownTest()
+
+ ctx.mockVpp.MockReply(&memif.MemifDeleteReply{
+ Retval: 24,
+ })
+ request := &memif.MemifDelete{
+ SwIfIndex: 15,
+ }
+ reply := &memif.MemifDeleteReply{}
+
+ err := ctx.ch.SendRequest(request).ReceiveReply(reply)
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(reply.Retval).To(BeEquivalentTo(24), "Incorrect Retval value for MemifDelete")
+}
+
+func TestRequestReplyMemifDetails(t *testing.T) {
+ ctx := setupTest(t)
+ defer ctx.teardownTest()
+
+ ctx.mockVpp.MockReply(&memif.MemifDetails{
+ SwIfIndex: 25,
+ IfName: []byte("memif-name"),
+ Role: 0,
+ })
+ request := &memif.MemifDump{}
+ reply := &memif.MemifDetails{}
+
+ err := ctx.ch.SendRequest(request).ReceiveReply(reply)
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(reply.SwIfIndex).To(BeEquivalentTo(25), "Incorrect SwIfIndex value for MemifDetails")
+ Expect(reply.IfName).ToNot(BeEmpty(), "MemifDetails IfName is empty byte array")
+ Expect(reply.Role).To(BeEquivalentTo(0), "Incorrect Role value for MemifDetails")
+}
+
+func TestMultiRequestReplySwInterfaceTapDump(t *testing.T) {
+ ctx := setupTest(t)
+ defer ctx.teardownTest()
+
+ // mock reply
+ var msgs []api.Message
+ for i := 1; i <= 10; i++ {
+ msgs = append(msgs, &tap.SwInterfaceTapDetails{
+ SwIfIndex: uint32(i),
+ DevName: []byte("dev-name-test"),
+ })
+ }
+ ctx.mockVpp.MockReply(msgs...)
+ ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
+
+ reqCtx := ctx.ch.SendMultiRequest(&tap.SwInterfaceTapDump{})
+ cnt := 0
+ for {
+ msg := &tap.SwInterfaceTapDetails{}
+ stop, err := reqCtx.ReceiveReply(msg)
+ if stop {
+ break // break out of the loop
+ }
+ Expect(err).ShouldNot(HaveOccurred())
+ cnt++
+ }
+ Expect(cnt).To(BeEquivalentTo(10))
+}
+
+func TestMultiRequestReplySwInterfaceMemifDump(t *testing.T) {
+ ctx := setupTest(t)
+ defer ctx.teardownTest()
+
+ // mock reply
+ var msgs []api.Message
+ for i := 1; i <= 10; i++ {
+ msgs = append(msgs, &memif.MemifDetails{
+ SwIfIndex: uint32(i),
+ })
+ }
+ ctx.mockVpp.MockReply(msgs...)
+ ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
+
+ reqCtx := ctx.ch.SendMultiRequest(&memif.MemifDump{})
+ cnt := 0
+ for {
+ msg := &memif.MemifDetails{}
+ stop, err := reqCtx.ReceiveReply(msg)
+ if stop {
+ break // break out of the loop
+ }
+ Expect(err).ShouldNot(HaveOccurred())
+ cnt++
+ }
+ Expect(cnt).To(BeEquivalentTo(10))
+}
+
+func TestNotifications(t *testing.T) {
+ ctx := setupTest(t)
+ defer ctx.teardownTest()
+
+ // subscribe for notification
+ notifChan := make(chan api.Message, 1)
+ subs, err := ctx.ch.SubscribeNotification(notifChan, interfaces.NewSwInterfaceSetFlags)
+ Expect(err).ShouldNot(HaveOccurred())
+
+ // mock the notification and force its delivery
+ ctx.mockVpp.MockReply(&interfaces.SwInterfaceSetFlags{
+ SwIfIndex: 3,
+ AdminUpDown: 1,
+ })
+ ctx.mockVpp.SendMsg(0, []byte(""))
+
+ // receive the notification
+ var notif *interfaces.SwInterfaceSetFlags
+ Eventually(func() *interfaces.SwInterfaceSetFlags {
+ select {
+ case n := <-notifChan:
+ notif = n.(*interfaces.SwInterfaceSetFlags)
+ return notif
+ default:
+ return nil
+ }
+ }).ShouldNot(BeNil())
+
+ // verify the received notifications
+ Expect(notif.SwIfIndex).To(BeEquivalentTo(3), "Incorrect SwIfIndex value for SwInterfaceSetFlags")
+ Expect(notif.AdminUpDown).To(BeEquivalentTo(1), "Incorrect AdminUpDown value for SwInterfaceSetFlags")
+
+ ctx.ch.UnsubscribeNotification(subs)
+}
+
+func TestNotificationEvent(t *testing.T) {
+ ctx := setupTest(t)
+ defer ctx.teardownTest()
+
+ // subscribe for notification
+ notifChan := make(chan api.Message, 1)
+ subs, err := ctx.ch.SubscribeNotification(notifChan, interfaces.NewSwInterfaceEvent)
+ Expect(err).ShouldNot(HaveOccurred())
+
+ // mock the notification and force its delivery
+ ctx.mockVpp.MockReply(&interfaces.SwInterfaceEvent{
+ SwIfIndex: 2,
+ LinkUpDown: 1,
+ })
+ ctx.mockVpp.SendMsg(0, []byte(""))
+
+ // receive the notification
+ var notif *interfaces.SwInterfaceEvent
+ Eventually(func() *interfaces.SwInterfaceEvent {
+ select {
+ case n := <-notifChan:
+ notif = n.(*interfaces.SwInterfaceEvent)
+ return notif
+ default:
+ return nil
+ }
+ }).ShouldNot(BeNil())
+
+ // verify the received notifications
+ Expect(notif.SwIfIndex).To(BeEquivalentTo(2), "Incorrect SwIfIndex value for SwInterfaceSetFlags")
+ Expect(notif.LinkUpDown).To(BeEquivalentTo(1), "Incorrect LinkUpDown value for SwInterfaceSetFlags")
+
+ ctx.ch.UnsubscribeNotification(subs)
+}
+
+func TestCheckMessageCompatibility(t *testing.T) {
+ ctx := setupTest(t)
+ defer ctx.teardownTest()
+
+ err := ctx.ch.CheckMessageCompatibility(&interfaces.SwInterfaceSetFlags{})
+ Expect(err).ShouldNot(HaveOccurred())
+}
+func TestSetReplyTimeout(t *testing.T) {
+ ctx := setupTest(t)
+ defer ctx.teardownTest()
+
+ ctx.ch.SetReplyTimeout(time.Millisecond)
+
+ // first one request should work
+ ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
+ err := ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
+ Expect(err).ShouldNot(HaveOccurred())
+
+ // no other reply ready - expect timeout
+ err = ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
+ Expect(err).Should(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("timeout"))
+}
+
+func TestSetReplyTimeoutMultiRequest(t *testing.T) {
+ ctx := setupTest(t)
+ defer ctx.teardownTest()
+
+ ctx.ch.SetReplyTimeout(time.Millisecond)
+
+ var msgs []api.Message
+ for i := 1; i <= 3; i++ {
+ msgs = append(msgs, &interfaces.SwInterfaceDetails{
+ SwIfIndex: uint32(i),
+ InterfaceName: []byte("if-name-test"),
+ })
+ }
+ ctx.mockVpp.MockReply(msgs...)
+ ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
+
+ cnt := 0
+ sendMultiRequest := func() error {
+ reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
+ for {
+ msg := &interfaces.SwInterfaceDetails{}
+ stop, err := reqCtx.ReceiveReply(msg)
+ if stop {
+ break // break out of the loop
+ }
+ if err != nil {
+ return err
+ }
+ cnt++
+ }
+ return nil
+ }
+
+ // first one request should work
+ err := sendMultiRequest()
+ Expect(err).ShouldNot(HaveOccurred())
+
+ // no other reply ready - expect timeout
+ err = sendMultiRequest()
+ Expect(err).Should(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("timeout"))
+
+ Expect(cnt).To(BeEquivalentTo(3))
+}
+
+func TestReceiveReplyNegative(t *testing.T) {
+ ctx := setupTest(t)
+ defer ctx.teardownTest()
+
+ // invalid context 1
+ reqCtx1 := &requestCtxData{}
+ err := reqCtx1.ReceiveReply(&vpe.ControlPingReply{})
+ Expect(err).Should(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("invalid request context"))
+
+ // invalid context 2
+ reqCtx2 := &multiRequestCtxData{}
+ _, err = reqCtx2.ReceiveReply(&vpe.ControlPingReply{})
+ Expect(err).Should(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("invalid request context"))
+
+ // NU
+ reqCtx3 := &requestCtxData{}
+ err = reqCtx3.ReceiveReply(nil)
+ Expect(err).Should(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("invalid request context"))
+}
+
+func TestMultiRequestDouble(t *testing.T) {
+ ctx := setupTest(t)
+ defer ctx.teardownTest()
+
+ // mock reply
+ var msgs []mock.MsgWithContext
+ for i := 1; i <= 3; i++ {
+ msgs = append(msgs, mock.MsgWithContext{
+ Msg: &interfaces.SwInterfaceDetails{
+ SwIfIndex: uint32(i),
+ InterfaceName: []byte("if-name-test"),
+ },
+ Multipart: true,
+ SeqNum: 1,
+ })
+ }
+ msgs = append(msgs, mock.MsgWithContext{Msg: &vpe.ControlPingReply{}, Multipart: true, SeqNum: 1})
+
+ for i := 1; i <= 3; i++ {
+ msgs = append(msgs,
+ mock.MsgWithContext{
+ Msg: &interfaces.SwInterfaceDetails{
+ SwIfIndex: uint32(i),
+ InterfaceName: []byte("if-name-test"),
+ },
+ Multipart: true,
+ SeqNum: 2,
+ })
+ }
+ msgs = append(msgs, mock.MsgWithContext{Msg: &vpe.ControlPingReply{}, Multipart: true, SeqNum: 2})
+
+ ctx.mockVpp.MockReplyWithContext(msgs...)
+
+ cnt := 0
+ var sendMultiRequest = func() error {
+ reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
+ for {
+ msg := &interfaces.SwInterfaceDetails{}
+ stop, err := reqCtx.ReceiveReply(msg)
+ if stop {
+ break // break out of the loop
+ }
+ if err != nil {
+ return err
+ }
+ cnt++
+ }
+ return nil
+ }
+
+ err := sendMultiRequest()
+ Expect(err).ShouldNot(HaveOccurred())
+
+ err = sendMultiRequest()
+ Expect(err).ShouldNot(HaveOccurred())
+
+ Expect(cnt).To(BeEquivalentTo(6))
+}
+
+func TestReceiveReplyAfterTimeout(t *testing.T) {
+ ctx := setupTest(t)
+ defer ctx.teardownTest()
+
+ ctx.ch.SetReplyTimeout(time.Millisecond)
+
+ // first one request should work
+ ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &vpe.ControlPingReply{}, SeqNum: 1})
+ err := ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
+ Expect(err).ShouldNot(HaveOccurred())
+
+ err = ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
+ Expect(err).Should(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("timeout"))
+
+ ctx.mockVpp.MockReplyWithContext(
+ // simulating late reply
+ mock.MsgWithContext{Msg: &vpe.ControlPingReply{}, SeqNum: 2},
+ // normal reply for next request
+ mock.MsgWithContext{Msg: &tap.TapConnectReply{}, SeqNum: 3})
+
+ req := &tap.TapConnect{
+ TapName: []byte("test-tap-name"),
+ UseRandomMac: 1,
+ }
+ reply := &tap.TapConnectReply{}
+
+ // should succeed
+ err = ctx.ch.SendRequest(req).ReceiveReply(reply)
+ Expect(err).ShouldNot(HaveOccurred())
+}
+
+func TestReceiveReplyAfterTimeoutMultiRequest(t *testing.T) {
+ /*
+ TODO: fix mock adapter
+ This test will fail because mock adapter will stop sending replies
+ when it encounters control_ping_reply from multi request,
+ thus never sending reply for next request
+ */
+ t.Skip()
+
+ ctx := setupTest(t)
+ defer ctx.teardownTest()
+
+ ctx.ch.SetReplyTimeout(time.Millisecond * 100)
+
+ // first one request should work
+ ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &vpe.ControlPingReply{}, SeqNum: 1})
+ err := ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
+ Expect(err).ShouldNot(HaveOccurred())
+
+ cnt := 0
+ var sendMultiRequest = func() error {
+ reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
+ for {
+ msg := &interfaces.SwInterfaceDetails{}
+ stop, err := reqCtx.ReceiveReply(msg)
+ if stop {
+ break // break out of the loop
+ }
+ if err != nil {
+ return err
+ }
+ cnt++
+ }
+ return nil
+ }
+
+ err = sendMultiRequest()
+ Expect(err).Should(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("timeout"))
+ Expect(cnt).To(BeEquivalentTo(0))
+
+ // simulating late replies
+ var msgs []mock.MsgWithContext
+ for i := 1; i <= 3; i++ {
+ msgs = append(msgs, mock.MsgWithContext{
+ Msg: &interfaces.SwInterfaceDetails{
+ SwIfIndex: uint32(i),
+ InterfaceName: []byte("if-name-test"),
+ },
+ Multipart: true,
+ SeqNum: 2,
+ })
+ }
+ msgs = append(msgs, mock.MsgWithContext{Msg: &vpe.ControlPingReply{}, Multipart: true, SeqNum: 2})
+ ctx.mockVpp.MockReplyWithContext(msgs...)
+
+ // normal reply for next request
+ ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &tap.TapConnectReply{}, SeqNum: 3})
+
+ req := &tap.TapConnect{
+ TapName: []byte("test-tap-name"),
+ UseRandomMac: 1,
+ }
+ reply := &tap.TapConnectReply{}
+
+ // should succeed
+ err = ctx.ch.SendRequest(req).ReceiveReply(reply)
+ Expect(err).ShouldNot(HaveOccurred())
+}
+
+func TestInvalidMessageID(t *testing.T) {
+ ctx := setupTest(t)
+ defer ctx.teardownTest()
+
+ // first one request should work
+ ctx.mockVpp.MockReply(&vpe.ShowVersionReply{})
+ err := ctx.ch.SendRequest(&vpe.ShowVersion{}).ReceiveReply(&vpe.ShowVersionReply{})
+ Expect(err).ShouldNot(HaveOccurred())
+
+ // second should fail with error invalid message ID
+ ctx.mockVpp.MockReply(&vpe.ShowVersionReply{})
+ err = ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
+ Expect(err).Should(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("invalid message ID"))
+}