"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"
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()
})
request := &memif.MemifCreate{
Role: 10,
- Key: 12,
+ ID: 12,
RingSize: 8000,
BufferSize: 50,
}
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{})
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{})
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()
err := ctx.ch.CheckMessageCompatibility(&interfaces.SwInterfaceSetFlags{})
Expect(err).ShouldNot(HaveOccurred())
}
-
func TestSetReplyTimeout(t *testing.T) {
ctx := setupTest(t)
defer ctx.teardownTest()
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()
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"))
+}