1 // Copyright (c) 2017 Cisco and/or its affiliates.
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at:
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
20 . "github.com/onsi/gomega"
22 "git.fd.io/govpp.git/adapter/mock"
23 "git.fd.io/govpp.git/api"
24 "git.fd.io/govpp.git/binapi/ethernet_types"
25 interfaces "git.fd.io/govpp.git/binapi/interface"
26 "git.fd.io/govpp.git/binapi/interface_types"
27 "git.fd.io/govpp.git/binapi/vpe"
28 "git.fd.io/govpp.git/codec"
29 "git.fd.io/govpp.git/core"
33 mockVpp *mock.VppAdapter
38 func setupTest(t *testing.T, bufferedChan bool) *testCtx {
42 mockVpp: mock.NewVppAdapter(),
46 ctx.conn, err = core.Connect(ctx.mockVpp)
47 Expect(err).ShouldNot(HaveOccurred())
50 ctx.ch, err = ctx.conn.NewAPIChannelBuffered(100, 100)
52 ctx.ch, err = ctx.conn.NewAPIChannel()
54 Expect(err).ShouldNot(HaveOccurred())
59 func (ctx *testCtx) teardownTest() {
64 func TestNilConnection(t *testing.T) {
66 var conn *core.Connection
68 ch, err := conn.NewAPIChannel()
69 Expect(ch).Should(BeNil())
70 Expect(err).Should(HaveOccurred())
71 Expect(err.Error()).To(ContainSubstring("nil"))
73 ch, err = conn.NewAPIChannelBuffered(1, 1)
74 Expect(ch).Should(BeNil())
75 Expect(err).Should(HaveOccurred())
76 Expect(err.Error()).To(ContainSubstring("nil"))
79 func TestAsyncConnection(t *testing.T) {
80 ctx := setupTest(t, false)
81 defer ctx.teardownTest()
84 conn, statusChan, err := core.AsyncConnect(ctx.mockVpp, core.DefaultMaxReconnectAttempts, core.DefaultReconnectInterval)
87 Expect(err).ShouldNot(HaveOccurred())
88 Expect(conn).ShouldNot(BeNil())
91 Expect(ev.State).Should(BeEquivalentTo(core.Connected))
94 func TestCodec(t *testing.T) {
97 var msgCodec = codec.DefaultCodec
100 data, err := msgCodec.EncodeMsg(&interfaces.CreateLoopback{MacAddress: ethernet_types.MacAddress{1, 2, 3, 4, 5, 6}}, 11)
101 Expect(err).ShouldNot(HaveOccurred())
102 Expect(data).ShouldNot(BeEmpty())
104 msg1 := &interfaces.CreateLoopback{}
105 err = msgCodec.DecodeMsg(data, msg1)
106 Expect(err).ShouldNot(HaveOccurred())
107 Expect(msg1.MacAddress).To(BeEquivalentTo(ethernet_types.MacAddress{1, 2, 3, 4, 5, 6}))
110 data, err = msgCodec.EncodeMsg(&vpe.ControlPingReply{Retval: 55}, 22)
111 Expect(err).ShouldNot(HaveOccurred())
112 Expect(data).ShouldNot(BeEmpty())
114 msg2 := &vpe.ControlPingReply{}
115 err = msgCodec.DecodeMsg(data, msg2)
116 Expect(err).ShouldNot(HaveOccurred())
117 Expect(msg2.Retval).To(BeEquivalentTo(55))
120 func TestCodecNegative(t *testing.T) {
123 var msgCodec = codec.DefaultCodec
125 // nil message for encoding
126 data, err := msgCodec.EncodeMsg(nil, 15)
127 Expect(err).Should(HaveOccurred())
128 Expect(err.Error()).To(ContainSubstring("nil message"))
129 Expect(data).Should(BeNil())
131 // nil message for decoding
132 err = msgCodec.DecodeMsg(data, nil)
133 Expect(err).Should(HaveOccurred())
134 Expect(err.Error()).To(ContainSubstring("nil message"))
136 // nil data for decoding
137 err = msgCodec.DecodeMsg(nil, &vpe.ControlPingReply{})
138 Expect(err).Should(HaveOccurred())
139 Expect(err.Error()).To(ContainSubstring("panic"))
142 func TestSimpleRequestsWithSequenceNumbers(t *testing.T) {
143 ctx := setupTest(t, false)
144 defer ctx.teardownTest()
146 var reqCtx []api.RequestCtx
147 for i := 0; i < 10; i++ {
148 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
149 req := &vpe.ControlPing{}
150 reqCtx = append(reqCtx, ctx.ch.SendRequest(req))
153 for i := 0; i < 10; i++ {
154 reply := &vpe.ControlPingReply{}
155 err := reqCtx[i].ReceiveReply(reply)
156 Expect(err).ShouldNot(HaveOccurred())
160 func TestMultiRequestsWithSequenceNumbers(t *testing.T) {
161 ctx := setupTest(t, false)
162 defer ctx.teardownTest()
164 var msgs []api.Message
165 for i := 0; i < 10; i++ {
166 msgs = append(msgs, &interfaces.SwInterfaceDetails{SwIfIndex: interface_types.InterfaceIndex(i)})
168 ctx.mockVpp.MockReply(msgs...)
169 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
171 // send multipart request
172 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
176 Expect(cnt < 11).To(BeTrue())
179 reply := &interfaces.SwInterfaceDetails{}
180 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
182 if lastReplyReceived {
186 Expect(err).ShouldNot(HaveOccurred())
187 Expect(reply.SwIfIndex).To(BeEquivalentTo(cnt))
192 Expect(cnt).To(BeEquivalentTo(10))
195 func TestSimpleRequestWithTimeout(t *testing.T) {
196 ctx := setupTest(t, true)
197 defer ctx.teardownTest()
199 // reply for a previous timeouted requests to be ignored
200 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
201 Msg: &vpe.ControlPingReply{},
206 req1 := &vpe.ControlPing{}
207 reqCtx1 := ctx.ch.SendRequest(req1)
209 reply := &vpe.ControlPingReply{}
210 err := reqCtx1.ReceiveReply(reply)
211 Expect(err).ToNot(BeNil())
212 Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
214 ctx.mockVpp.MockReplyWithContext(
215 // reply for the previous request
217 Msg: &vpe.ControlPingReply{},
220 // reply for the next request
222 Msg: &vpe.ControlPingReply{},
227 req2 := &vpe.ControlPing{}
228 reqCtx2 := ctx.ch.SendRequest(req2)
230 // second request should ignore the first reply and return the second one
231 reply = &vpe.ControlPingReply{}
232 err = reqCtx2.ReceiveReply(reply)
233 Expect(err).To(BeNil())
236 func TestSimpleRequestsWithMissingReply(t *testing.T) {
237 ctx := setupTest(t, false)
238 defer ctx.teardownTest()
240 // request without reply
241 req1 := &vpe.ControlPing{}
242 reqCtx1 := ctx.ch.SendRequest(req1)
244 // another request without reply
245 req2 := &vpe.ControlPing{}
246 reqCtx2 := ctx.ch.SendRequest(req2)
248 // third request with reply
249 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
250 Msg: &vpe.ControlPingReply{},
253 req3 := &vpe.ControlPing{}
254 reqCtx3 := ctx.ch.SendRequest(req3)
256 // the first two should fail, but not consume reply for the 3rd
257 reply := &vpe.ControlPingReply{}
258 err := reqCtx1.ReceiveReply(reply)
259 Expect(err).ToNot(BeNil())
260 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
262 reply = &vpe.ControlPingReply{}
263 err = reqCtx2.ReceiveReply(reply)
264 Expect(err).ToNot(BeNil())
265 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 2"))
267 // the second request should succeed
268 reply = &vpe.ControlPingReply{}
269 err = reqCtx3.ReceiveReply(reply)
270 Expect(err).To(BeNil())
273 func TestMultiRequestsWithErrors(t *testing.T) {
274 ctx := setupTest(t, false)
275 defer ctx.teardownTest()
277 // replies for a previous timeouted requests to be ignored
278 msgs := []mock.MsgWithContext{
279 {Msg: &vpe.ControlPingReply{}, SeqNum: 0xffff - 1},
280 {Msg: &vpe.ControlPingReply{}, SeqNum: 0xffff},
281 {Msg: &vpe.ControlPingReply{}, SeqNum: 0},
283 for i := 0; i < 10; i++ {
284 msgs = append(msgs, mock.MsgWithContext{
285 Msg: &interfaces.SwInterfaceDetails{SwIfIndex: interface_types.InterfaceIndex(i)},
290 // missing finalizing control ping
292 // reply for a next request
293 msgs = append(msgs, mock.MsgWithContext{
294 Msg: &vpe.ControlPingReply{},
299 ctx.mockVpp.MockReplyWithContext(msgs...)
301 // send multipart request
302 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
304 for i := 0; i < 10; i++ {
305 // receive multi-part replies
306 reply := &interfaces.SwInterfaceDetails{}
307 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
309 Expect(lastReplyReceived).To(BeFalse())
310 Expect(err).ShouldNot(HaveOccurred())
311 Expect(reply.SwIfIndex).To(BeEquivalentTo(i))
314 // missing closing control ping
315 reply := &interfaces.SwInterfaceDetails{}
316 _, err := reqCtx.ReceiveReply(reply)
317 Expect(err).ToNot(BeNil())
318 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
320 // try again - still fails and nothing consumed
321 _, err = reqCtx.ReceiveReply(reply)
322 Expect(err).ToNot(BeNil())
323 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
325 // reply for the second request has not been consumed
326 reqCtx2 := ctx.ch.SendRequest(&vpe.ControlPing{})
327 reply2 := &vpe.ControlPingReply{}
328 err = reqCtx2.ReceiveReply(reply2)
329 Expect(err).To(BeNil())
332 func TestRequestsOrdering(t *testing.T) {
333 ctx := setupTest(t, false)
334 defer ctx.teardownTest()
336 // the orderings of SendRequest and ReceiveReply calls should match, otherwise
337 // some replies will get thrown away
340 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
341 req1 := &vpe.ControlPing{}
342 reqCtx1 := ctx.ch.SendRequest(req1)
345 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
346 req2 := &vpe.ControlPing{}
347 reqCtx2 := ctx.ch.SendRequest(req2)
349 // if reply for the second request is read first, the reply for the first
350 // request gets thrown away.
351 reply2 := &vpe.ControlPingReply{}
352 err := reqCtx2.ReceiveReply(reply2)
353 Expect(err).To(BeNil())
355 // first request has already been considered closed
356 reply1 := &vpe.ControlPingReply{}
357 err = reqCtx1.ReceiveReply(reply1)
358 Expect(err).ToNot(BeNil())
359 Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
362 func TestCycleOverSetOfSequenceNumbers(t *testing.T) {
363 ctx := setupTest(t, false)
364 defer ctx.teardownTest()
366 numIters := 0xffff + 100
367 reqCtx := make(map[int]api.RequestCtx)
369 for i := 0; i < numIters+30; i++ {
371 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
372 req := &vpe.ControlPing{}
373 reqCtx[i] = ctx.ch.SendRequest(req)
376 reply := &vpe.ControlPingReply{}
377 err := reqCtx[i-30].ReceiveReply(reply)
378 Expect(err).ShouldNot(HaveOccurred())