mock adapter: Group all replies for one request under one call to MockReply
[govpp.git] / api / api_test.go
index 3e77f48..7cbd9f0 100644 (file)
@@ -196,13 +196,14 @@ func TestMultiRequestReplySwInterfaceTapDump(t *testing.T) {
        defer ctx.teardownTest()
 
        // mock reply
+       msgs := []api.Message{}
        for i := 1; i <= 10; i++ {
-               byteName := []byte("dev-name-test")
-               ctx.mockVpp.MockReply(&tap.SwInterfaceTapDetails{
+               msgs = append(msgs, &tap.SwInterfaceTapDetails{
                        SwIfIndex: uint32(i),
-                       DevName:   byteName,
+                       DevName:   []byte("dev-name-test"),
                })
        }
+       ctx.mockVpp.MockReply(msgs...)
        ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
 
        reqCtx := ctx.ch.SendMultiRequest(&tap.SwInterfaceTapDump{})
@@ -224,11 +225,13 @@ func TestMultiRequestReplySwInterfaceMemifDump(t *testing.T) {
        defer ctx.teardownTest()
 
        // mock reply
+       msgs := []api.Message{}
        for i := 1; i <= 10; i++ {
-               ctx.mockVpp.MockReply(&memif.MemifDetails{
+               msgs = append(msgs, &memif.MemifDetails{
                        SwIfIndex: uint32(i),
                })
        }
+       ctx.mockVpp.MockReply(msgs...)
        ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
 
        reqCtx := ctx.ch.SendMultiRequest(&memif.MemifDump{})
@@ -322,7 +325,6 @@ func TestCheckMessageCompatibility(t *testing.T) {
        err := ctx.ch.CheckMessageCompatibility(&interfaces.SwInterfaceSetFlags{})
        Expect(err).ShouldNot(HaveOccurred())
 }
-
 func TestSetReplyTimeout(t *testing.T) {
        ctx := setupTest(t)
        defer ctx.teardownTest()
@@ -340,6 +342,51 @@ 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)
+
+       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()
@@ -362,3 +409,180 @@ 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
+       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
+       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"))
+}