Ignore invalid message ID if last request timed out
[govpp.git] / api / api_test.go
index e7592e0..62541ab 100644 (file)
@@ -18,7 +18,6 @@ import (
        "testing"
        "time"
 
-       "git.fd.io/govpp.git"
        "git.fd.io/govpp.git/adapter/mock"
        "git.fd.io/govpp.git/api"
        "git.fd.io/govpp.git/core"
@@ -39,12 +38,12 @@ type testCtx struct {
 func setupTest(t *testing.T) *testCtx {
        RegisterTestingT(t)
 
-       ctx := &testCtx{}
-       ctx.mockVpp = &mock.VppAdapter{}
-       govpp.SetAdapter(ctx.mockVpp)
+       ctx := &testCtx{
+               mockVpp: &mock.VppAdapter{},
+       }
 
        var err error
-       ctx.conn, err = govpp.Connect()
+       ctx.conn, err = core.Connect(ctx.mockVpp)
        Expect(err).ShouldNot(HaveOccurred())
 
        ctx.ch, err = ctx.conn.NewAPIChannel()
@@ -144,7 +143,7 @@ func TestRequestReplyMemifCreate(t *testing.T) {
        })
        request := &memif.MemifCreate{
                Role:       10,
-               Key:        12,
+               ID:         12,
                RingSize:   8000,
                BufferSize: 50,
        }
@@ -198,10 +197,9 @@ func TestMultiRequestReplySwInterfaceTapDump(t *testing.T) {
 
        // mock reply
        for i := 1; i <= 10; i++ {
-               byteName := []byte("dev-name-test")
                ctx.mockVpp.MockReply(&tap.SwInterfaceTapDetails{
                        SwIfIndex: uint32(i),
-                       DevName:   byteName,
+                       DevName:   []byte("dev-name-test"),
                })
        }
        ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
@@ -263,16 +261,59 @@ func TestNotifications(t *testing.T) {
        ctx.mockVpp.SendMsg(0, []byte(""))
 
        // receive the notification
-       notif := (<-notifChan).(*interfaces.SwInterfaceSetFlags)
+       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).ShouldNot(BeNil())
        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()
@@ -298,6 +339,49 @@ func TestSetReplyTimeout(t *testing.T) {
        Expect(err.Error()).To(ContainSubstring("timeout"))
 }
 
+func TestSetReplyTimeoutMultiRequest(t *testing.T) {
+       ctx := setupTest(t)
+       defer ctx.teardownTest()
+
+       ctx.ch.SetReplyTimeout(time.Millisecond)
+
+       for i := 1; i <= 3; i++ {
+               ctx.mockVpp.MockReply(&interfaces.SwInterfaceDetails{
+                       SwIfIndex:     uint32(i),
+                       InterfaceName: []byte("if-name-test"),
+               })
+       }
+       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()
@@ -320,3 +404,144 @@ func TestReceiveReplyNegative(t *testing.T) {
        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
+       for i := 1; i <= 3; i++ {
+               ctx.mockVpp.MockReply(&interfaces.SwInterfaceDetails{
+                       SwIfIndex:     uint32(i),
+                       InterfaceName: []byte("if-name-test"),
+               })
+       }
+       ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
+       for i := 1; i <= 3; i++ {
+               ctx.mockVpp.MockReply(&interfaces.SwInterfaceDetails{
+                       SwIfIndex:     uint32(i),
+                       InterfaceName: []byte("if-name-test"),
+               })
+       }
+       ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
+
+       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.MockReply(&vpe.ControlPingReply{})
+       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"))
+
+       // simulating late reply
+       ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
+
+       // normal reply for next request
+       ctx.mockVpp.MockReply(&tap.TapConnectReply{})
+
+       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())
+}
+
+/*
+       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
+
+func TestReceiveReplyAfterTimeoutMultiRequest(t *testing.T) {
+       ctx := setupTest(t)
+       defer ctx.teardownTest()
+
+       ctx.ch.SetReplyTimeout(time.Millisecond * 100)
+
+       // first one request should work
+       ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
+       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
+       for i := 1; i <= 3; i++ {
+               ctx.mockVpp.MockReply(&interfaces.SwInterfaceDetails{
+                       SwIfIndex:     uint32(i),
+                       InterfaceName: []byte("if-name-test"),
+               })
+       }
+       ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
+
+       // normal reply for next request
+       ctx.mockVpp.MockReply(&tap.TapConnectReply{})
+
+       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())
+}
+*/