aboutsummaryrefslogtreecommitdiffstats
path: root/api
diff options
context:
space:
mode:
authorMilan Lenco <milan.lenco@pantheon.tech>2018-06-27 15:55:43 +0200
committerMilan Lenco <milan.lenco@pantheon.tech>2018-06-27 16:20:55 +0200
commit5276b9439d0f902e125a5113bfa4f1b6622aea18 (patch)
tree012189945f413ce7f78528ea348edc41f6a11326 /api
parent8adb6cdcb496f05169263d32a857791faf8baee1 (diff)
mock adapter: Group all replies for one request under one call to MockReply
Sequence numbers are now used to match requests with replies. Mock adapter thus has to be able to tell how many messages from the head of the queue with mock replies belong to the currently processed request. Then they can be given the right context and the rest of the queued replies are postponed to be delivered later (when context of their request is known). All replies for one request are now therefore queued together. This affects just multipart requests for which replies have to be pushed all at once. The trailling control ping reply is still queued separately, however, because that is actualy another request, e.g.: mockVpp.MockReply( // push multipart messages all at once &interfaces.SwInterfaceDetails{SwIfIndex:1}, &interfaces.SwInterfaceDetails{SwIfIndex:2}, &interfaces.SwInterfaceDetails{SwIfIndex:3}, ) mockVpp.MockReply(&vpe.ControlPingReply{}) Even if the multipart request has no replies, MockReply has to be called exactly twice: mockVpp.MockReply() // zero multipart messages mockVpp.MockReply(&vpe.ControlPingReply{}) Change-Id: I28c15d2f52d14dca0b7fb06033d7270a7da2bde6 Signed-off-by: Milan Lenco <milan.lenco@pantheon.tech>
Diffstat (limited to 'api')
-rw-r--r--api/api.go16
-rw-r--r--api/api_test.go113
2 files changed, 77 insertions, 52 deletions
diff --git a/api/api.go b/api/api.go
index 3c2c7ec..34e17c1 100644
--- a/api/api.go
+++ b/api/api.go
@@ -87,7 +87,7 @@ type MessageIdentifier interface {
// methods provided inside of this package. Do not use the same channel from multiple goroutines concurrently,
// otherwise the responses could mix! Use multiple channels instead.
type Channel struct {
- ID uint16 // channel ID
+ ID uint16 // channel ID
ReqChan chan *VppRequest // channel for sending the requests to VPP, closing this channel releases all resources in the ChannelProvider
ReplyChan chan *VppReply // channel where VPP replies are delivered to
@@ -134,13 +134,13 @@ type NotifSubscription struct {
// RequestCtx is a context of a ongoing request (simple one - only one response is expected).
type RequestCtx struct {
- ch *Channel
+ ch *Channel
seqNum uint16
}
// MultiRequestCtx is a context of a ongoing multipart request (multiple responses are expected).
type MultiRequestCtx struct {
- ch *Channel
+ ch *Channel
seqNum uint16
}
@@ -151,7 +151,7 @@ const defaultReplyTimeout = time.Second * 1 // default timeout for replies from
// Use ChannelProvider to get an API channel ready for communication with VPP.
func NewChannelInternal(id uint16) *Channel {
return &Channel{
- ID: id,
+ ID: id,
replyTimeout: defaultReplyTimeout,
}
}
@@ -175,7 +175,7 @@ func (ch *Channel) SendRequest(msg Message) *RequestCtx {
ch.lastSeqNum++
ch.ReqChan <- &VppRequest{
Message: msg,
- SeqNum: ch.lastSeqNum,
+ SeqNum: ch.lastSeqNum,
}
return &RequestCtx{ch: ch, seqNum: ch.lastSeqNum}
}
@@ -266,7 +266,7 @@ func (ch *Channel) processReply(reply *VppReply, expSeqNum uint16, msg Message)
ignore = true
return
}
- if cmpSeqNums == 1 {
+ if cmpSeqNums == 1 {
ch.delayedReply = reply
err = fmt.Errorf("missing binary API reply with sequence number: %d", expSeqNum)
return
@@ -298,8 +298,8 @@ func (ch *Channel) processReply(reply *VppReply, expSeqNum uint16, msg Message)
msgNameCrc = nameCrc
}
- err = fmt.Errorf("received invalid message ID (seq-num=%d), expected %d (%s), but got %d (%s) " +
- "(check if multiple goroutines are not sharing single GoVPP channel)",
+ err = fmt.Errorf("received invalid message ID (seq-num=%d), expected %d (%s), but got %d (%s) "+
+ "(check if multiple goroutines are not sharing single GoVPP channel)",
reply.SeqNum, expMsgID, msg.GetMessageName(), reply.MessageID, msgNameCrc)
return
}
diff --git a/api/api_test.go b/api/api_test.go
index a83b1cc..7cbd9f0 100644
--- a/api/api_test.go
+++ b/api/api_test.go
@@ -64,7 +64,7 @@ func TestRequestReplyTapConnect(t *testing.T) {
ctx.mockVpp.MockReply(&tap.TapConnectReply{
Retval: 10,
SwIfIndex: 1,
- }, false)
+ })
request := &tap.TapConnect{
TapName: []byte("test-tap-name"),
UseRandomMac: 1,
@@ -84,7 +84,7 @@ func TestRequestReplyTapModify(t *testing.T) {
ctx.mockVpp.MockReply(&tap.TapModifyReply{
Retval: 15,
SwIfIndex: 2,
- }, false)
+ })
request := &tap.TapModify{
TapName: []byte("test-tap-modify"),
UseRandomMac: 1,
@@ -104,7 +104,7 @@ func TestRequestReplyTapDelete(t *testing.T) {
ctx.mockVpp.MockReply(&tap.TapDeleteReply{
Retval: 20,
- }, false)
+ })
request := &tap.TapDelete{
SwIfIndex: 3,
}
@@ -123,7 +123,7 @@ func TestRequestReplySwInterfaceTapDump(t *testing.T) {
ctx.mockVpp.MockReply(&tap.SwInterfaceTapDetails{
SwIfIndex: 25,
DevName: byteName,
- }, false)
+ })
request := &tap.SwInterfaceTapDump{}
reply := &tap.SwInterfaceTapDetails{}
@@ -140,7 +140,7 @@ func TestRequestReplyMemifCreate(t *testing.T) {
ctx.mockVpp.MockReply(&memif.MemifCreateReply{
Retval: 22,
SwIfIndex: 4,
- }, false)
+ })
request := &memif.MemifCreate{
Role: 10,
ID: 12,
@@ -161,7 +161,7 @@ func TestRequestReplyMemifDelete(t *testing.T) {
ctx.mockVpp.MockReply(&memif.MemifDeleteReply{
Retval: 24,
- }, false)
+ })
request := &memif.MemifDelete{
SwIfIndex: 15,
}
@@ -180,7 +180,7 @@ func TestRequestReplyMemifDetails(t *testing.T) {
SwIfIndex: 25,
IfName: []byte("memif-name"),
Role: 0,
- }, false)
+ })
request := &memif.MemifDump{}
reply := &memif.MemifDetails{}
@@ -196,13 +196,15 @@ func TestMultiRequestReplySwInterfaceTapDump(t *testing.T) {
defer ctx.teardownTest()
// mock reply
+ msgs := []api.Message{}
for i := 1; i <= 10; i++ {
- ctx.mockVpp.MockReply(&tap.SwInterfaceTapDetails{
+ msgs = append(msgs, &tap.SwInterfaceTapDetails{
SwIfIndex: uint32(i),
DevName: []byte("dev-name-test"),
- }, true)
+ })
}
- ctx.mockVpp.MockReply(&vpe.ControlPingReply{}, true)
+ ctx.mockVpp.MockReply(msgs...)
+ ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
reqCtx := ctx.ch.SendMultiRequest(&tap.SwInterfaceTapDump{})
cnt := 0
@@ -223,12 +225,14 @@ 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),
- }, true)
+ })
}
- ctx.mockVpp.MockReply(&vpe.ControlPingReply{}, true)
+ ctx.mockVpp.MockReply(msgs...)
+ ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
reqCtx := ctx.ch.SendMultiRequest(&memif.MemifDump{})
cnt := 0
@@ -257,7 +261,7 @@ func TestNotifications(t *testing.T) {
ctx.mockVpp.MockReply(&interfaces.SwInterfaceSetFlags{
SwIfIndex: 3,
AdminUpDown: 1,
- }, false)
+ })
ctx.mockVpp.SendMsg(0, []byte(""))
// receive the notification
@@ -292,7 +296,7 @@ func TestNotificationEvent(t *testing.T) {
ctx.mockVpp.MockReply(&interfaces.SwInterfaceEvent{
SwIfIndex: 2,
LinkUpDown: 1,
- }, false)
+ })
ctx.mockVpp.SendMsg(0, []byte(""))
// receive the notification
@@ -328,7 +332,7 @@ func TestSetReplyTimeout(t *testing.T) {
ctx.ch.SetReplyTimeout(time.Millisecond)
// first one request should work
- ctx.mockVpp.MockReply(&vpe.ControlPingReply{}, false)
+ ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
err := ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
Expect(err).ShouldNot(HaveOccurred())
@@ -344,13 +348,15 @@ func TestSetReplyTimeoutMultiRequest(t *testing.T) {
ctx.ch.SetReplyTimeout(time.Millisecond)
+ msgs := []api.Message{}
for i := 1; i <= 3; i++ {
- ctx.mockVpp.MockReply(&interfaces.SwInterfaceDetails{
+ msgs = append(msgs, &interfaces.SwInterfaceDetails{
SwIfIndex: uint32(i),
InterfaceName: []byte("if-name-test"),
- }, true)
+ })
}
- ctx.mockVpp.MockReply(&vpe.ControlPingReply{}, true)
+ ctx.mockVpp.MockReply(msgs...)
+ ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
cnt := 0
sendMultiRequest := func() error {
@@ -409,20 +415,33 @@ func TestMultiRequestDouble(t *testing.T) {
defer ctx.teardownTest()
// mock reply
+ msgs := []mock.MsgWithContext{}
for i := 1; i <= 3; i++ {
- ctx.mockVpp.MockReplyWithSeqNum(&interfaces.SwInterfaceDetails{
- SwIfIndex: uint32(i),
- InterfaceName: []byte("if-name-test"),
- }, true, 1)
+ msgs = append(msgs, mock.MsgWithContext{
+ Msg: &interfaces.SwInterfaceDetails{
+ SwIfIndex: uint32(i),
+ InterfaceName: []byte("if-name-test"),
+ },
+ Multipart: true,
+ SeqNum: 1,
+ })
}
- ctx.mockVpp.MockReplyWithSeqNum(&vpe.ControlPingReply{}, true, 1)
+ msgs = append(msgs, mock.MsgWithContext{Msg: &vpe.ControlPingReply{}, Multipart: true, SeqNum: 1})
+
for i := 1; i <= 3; i++ {
- ctx.mockVpp.MockReplyWithSeqNum(&interfaces.SwInterfaceDetails{
- SwIfIndex: uint32(i),
- InterfaceName: []byte("if-name-test"),
- }, true, 2)
+ msgs = append(msgs,
+ mock.MsgWithContext{
+ Msg: &interfaces.SwInterfaceDetails{
+ SwIfIndex: uint32(i),
+ InterfaceName: []byte("if-name-test"),
+ },
+ Multipart: true,
+ SeqNum: 2,
+ })
}
- ctx.mockVpp.MockReplyWithSeqNum(&vpe.ControlPingReply{}, true,2)
+ msgs = append(msgs, mock.MsgWithContext{Msg: &vpe.ControlPingReply{}, Multipart: true, SeqNum: 2})
+
+ ctx.mockVpp.MockReplyWithContext(msgs...)
cnt := 0
var sendMultiRequest = func() error {
@@ -457,7 +476,7 @@ func TestReceiveReplyAfterTimeout(t *testing.T) {
ctx.ch.SetReplyTimeout(time.Millisecond)
// first one request should work
- ctx.mockVpp.MockReplyWithSeqNum(&vpe.ControlPingReply{}, false,1)
+ ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &vpe.ControlPingReply{}, SeqNum: 1})
err := ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
Expect(err).ShouldNot(HaveOccurred())
@@ -465,11 +484,11 @@ func TestReceiveReplyAfterTimeout(t *testing.T) {
Expect(err).Should(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("timeout"))
- // simulating late reply
- ctx.mockVpp.MockReplyWithSeqNum(&vpe.ControlPingReply{}, false,2)
-
- // normal reply for next request
- ctx.mockVpp.MockReplyWithSeqNum(&tap.TapConnectReply{}, false,3)
+ 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"),
@@ -497,7 +516,7 @@ func TestReceiveReplyAfterTimeoutMultiRequest(t *testing.T) {
ctx.ch.SetReplyTimeout(time.Millisecond * 100)
// first one request should work
- ctx.mockVpp.MockReplyWithSeqNum(&vpe.ControlPingReply{}, false, 1)
+ ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &vpe.ControlPingReply{}, SeqNum: 1})
err := ctx.ch.SendRequest(&vpe.ControlPing{}).ReceiveReply(&vpe.ControlPingReply{})
Expect(err).ShouldNot(HaveOccurred())
@@ -524,16 +543,22 @@ func TestReceiveReplyAfterTimeoutMultiRequest(t *testing.T) {
Expect(cnt).To(BeEquivalentTo(0))
// simulating late replies
+ msgs := []mock.MsgWithContext{}
for i := 1; i <= 3; i++ {
- ctx.mockVpp.MockReplyWithSeqNum(&interfaces.SwInterfaceDetails{
- SwIfIndex: uint32(i),
- InterfaceName: []byte("if-name-test"),
- }, true, 2)
+ msgs = append(msgs, mock.MsgWithContext{
+ Msg: &interfaces.SwInterfaceDetails{
+ SwIfIndex: uint32(i),
+ InterfaceName: []byte("if-name-test"),
+ },
+ Multipart: true,
+ SeqNum: 2,
+ })
}
- ctx.mockVpp.MockReplyWithSeqNum(&vpe.ControlPingReply{}, true, 2)
+ msgs = append(msgs, mock.MsgWithContext{Msg: &vpe.ControlPingReply{}, Multipart: true, SeqNum: 2})
+ ctx.mockVpp.MockReplyWithContext(msgs...)
// normal reply for next request
- ctx.mockVpp.MockReplyWithSeqNum(&tap.TapConnectReply{}, false, 3)
+ ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &tap.TapConnectReply{}, SeqNum: 3})
req := &tap.TapConnect{
TapName: []byte("test-tap-name"),
@@ -551,12 +576,12 @@ func TestInvalidMessageID(t *testing.T) {
defer ctx.teardownTest()
// first one request should work
- ctx.mockVpp.MockReply(&vpe.ShowVersionReply{}, false)
+ 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{}, false)
+ 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"))