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 "git.fd.io/govpp.git/adapter/mock"
21 "git.fd.io/govpp.git/api"
22 "git.fd.io/govpp.git/codec"
23 "git.fd.io/govpp.git/core"
24 "git.fd.io/govpp.git/examples/bin_api/interfaces"
25 "git.fd.io/govpp.git/examples/binapi/stats"
26 "git.fd.io/govpp.git/examples/binapi/vpe"
27 . "github.com/onsi/gomega"
31 mockVpp *mock.VppAdapter
36 func setupTest(t *testing.T, bufferedChan bool) *testCtx {
40 mockVpp: mock.NewVppAdapter(),
44 ctx.conn, err = core.Connect(ctx.mockVpp)
45 Expect(err).ShouldNot(HaveOccurred())
48 ctx.ch, err = ctx.conn.NewAPIChannelBuffered(100, 100)
50 ctx.ch, err = ctx.conn.NewAPIChannel()
52 Expect(err).ShouldNot(HaveOccurred())
57 func (ctx *testCtx) teardownTest() {
62 func TestNilConnection(t *testing.T) {
64 var conn *core.Connection
66 ch, err := conn.NewAPIChannel()
67 Expect(ch).Should(BeNil())
68 Expect(err).Should(HaveOccurred())
69 Expect(err.Error()).To(ContainSubstring("nil"))
71 ch, err = conn.NewAPIChannelBuffered(1, 1)
72 Expect(ch).Should(BeNil())
73 Expect(err).Should(HaveOccurred())
74 Expect(err.Error()).To(ContainSubstring("nil"))
77 func TestDoubleConnection(t *testing.T) {
78 ctx := setupTest(t, false)
79 defer ctx.teardownTest()
81 conn, err := core.Connect(ctx.mockVpp)
82 Expect(err).Should(HaveOccurred())
83 Expect(err.Error()).To(ContainSubstring("only one connection per process"))
84 Expect(conn).Should(BeNil())
87 func TestAsyncConnection(t *testing.T) {
88 ctx := setupTest(t, false)
89 defer ctx.teardownTest()
92 conn, statusChan, err := core.AsyncConnect(ctx.mockVpp)
95 Expect(err).ShouldNot(HaveOccurred())
96 Expect(conn).ShouldNot(BeNil())
99 Expect(ev.State).Should(BeEquivalentTo(core.Connected))
102 func TestCodec(t *testing.T) {
105 msgCodec := &codec.MsgCodec{}
108 data, err := msgCodec.EncodeMsg(&interfaces.CreateLoopback{MacAddress: []byte{1, 2, 3, 4, 5, 6}}, 11)
109 Expect(err).ShouldNot(HaveOccurred())
110 Expect(data).ShouldNot(BeEmpty())
112 msg1 := &interfaces.CreateLoopback{}
113 err = msgCodec.DecodeMsg(data, msg1)
114 Expect(err).ShouldNot(HaveOccurred())
115 Expect(msg1.MacAddress).To(BeEquivalentTo([]byte{1, 2, 3, 4, 5, 6}))
118 data, err = msgCodec.EncodeMsg(&vpe.ControlPingReply{Retval: 55}, 22)
119 Expect(err).ShouldNot(HaveOccurred())
120 Expect(data).ShouldNot(BeEmpty())
122 msg2 := &vpe.ControlPingReply{}
123 err = msgCodec.DecodeMsg(data, msg2)
124 Expect(err).ShouldNot(HaveOccurred())
125 Expect(msg2.Retval).To(BeEquivalentTo(55))
128 data, err = msgCodec.EncodeMsg(&stats.VnetIP4FibCounters{VrfID: 77}, 33)
129 Expect(err).ShouldNot(HaveOccurred())
130 Expect(data).ShouldNot(BeEmpty())
132 msg3 := &stats.VnetIP4FibCounters{}
133 err = msgCodec.DecodeMsg(data, msg3)
134 Expect(err).ShouldNot(HaveOccurred())
135 Expect(msg3.VrfID).To(BeEquivalentTo(77))
138 func TestCodecNegative(t *testing.T) {
141 msgCodec := &codec.MsgCodec{}
143 // nil message for encoding
144 data, err := msgCodec.EncodeMsg(nil, 15)
145 Expect(err).Should(HaveOccurred())
146 Expect(err.Error()).To(ContainSubstring("nil message"))
147 Expect(data).Should(BeNil())
149 // nil message for decoding
150 err = msgCodec.DecodeMsg(data, nil)
151 Expect(err).Should(HaveOccurred())
152 Expect(err.Error()).To(ContainSubstring("nil message"))
154 // nil data for decoding
155 err = msgCodec.DecodeMsg(nil, &vpe.ControlPingReply{})
156 Expect(err).Should(HaveOccurred())
157 Expect(err.Error()).To(ContainSubstring("EOF"))
160 func TestSimpleRequestsWithSequenceNumbers(t *testing.T) {
161 ctx := setupTest(t, false)
162 defer ctx.teardownTest()
164 var reqCtx []api.RequestCtx
165 for i := 0; i < 10; i++ {
166 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
167 req := &vpe.ControlPing{}
168 reqCtx = append(reqCtx, ctx.ch.SendRequest(req))
171 for i := 0; i < 10; i++ {
172 reply := &vpe.ControlPingReply{}
173 err := reqCtx[i].ReceiveReply(reply)
174 Expect(err).ShouldNot(HaveOccurred())
178 func TestMultiRequestsWithSequenceNumbers(t *testing.T) {
179 ctx := setupTest(t, false)
180 defer ctx.teardownTest()
182 var msgs []api.Message
183 for i := 0; i < 10; i++ {
184 msgs = append(msgs, &interfaces.SwInterfaceDetails{SwIfIndex: uint32(i)})
186 ctx.mockVpp.MockReply(msgs...)
187 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
189 // send multipart request
190 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
194 Expect(cnt < 11).To(BeTrue())
197 reply := &interfaces.SwInterfaceDetails{}
198 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
200 if lastReplyReceived {
204 Expect(err).ShouldNot(HaveOccurred())
205 Expect(reply.SwIfIndex).To(BeEquivalentTo(cnt))
210 Expect(cnt).To(BeEquivalentTo(10))
213 func TestSimpleRequestWithTimeout(t *testing.T) {
214 ctx := setupTest(t, true)
215 defer ctx.teardownTest()
217 // reply for a previous timeouted requests to be ignored
218 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
219 Msg: &vpe.ControlPingReply{},
224 req1 := &vpe.ControlPing{}
225 reqCtx1 := ctx.ch.SendRequest(req1)
227 reply := &vpe.ControlPingReply{}
228 err := reqCtx1.ReceiveReply(reply)
229 Expect(err).ToNot(BeNil())
230 Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
232 ctx.mockVpp.MockReplyWithContext(
233 // reply for the previous request
235 Msg: &vpe.ControlPingReply{},
238 // reply for the next request
240 Msg: &vpe.ControlPingReply{},
245 req2 := &vpe.ControlPing{}
246 reqCtx2 := ctx.ch.SendRequest(req2)
248 // second request should ignore the first reply and return the second one
249 reply = &vpe.ControlPingReply{}
250 err = reqCtx2.ReceiveReply(reply)
251 Expect(err).To(BeNil())
254 func TestSimpleRequestsWithMissingReply(t *testing.T) {
255 ctx := setupTest(t, false)
256 defer ctx.teardownTest()
258 // request without reply
259 req1 := &vpe.ControlPing{}
260 reqCtx1 := ctx.ch.SendRequest(req1)
262 // another request without reply
263 req2 := &vpe.ControlPing{}
264 reqCtx2 := ctx.ch.SendRequest(req2)
266 // third request with reply
267 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
268 Msg: &vpe.ControlPingReply{},
271 req3 := &vpe.ControlPing{}
272 reqCtx3 := ctx.ch.SendRequest(req3)
274 // the first two should fail, but not consume reply for the 3rd
275 reply := &vpe.ControlPingReply{}
276 err := reqCtx1.ReceiveReply(reply)
277 Expect(err).ToNot(BeNil())
278 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
280 reply = &vpe.ControlPingReply{}
281 err = reqCtx2.ReceiveReply(reply)
282 Expect(err).ToNot(BeNil())
283 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 2"))
285 // the second request should succeed
286 reply = &vpe.ControlPingReply{}
287 err = reqCtx3.ReceiveReply(reply)
288 Expect(err).To(BeNil())
291 func TestMultiRequestsWithErrors(t *testing.T) {
292 ctx := setupTest(t, false)
293 defer ctx.teardownTest()
295 // replies for a previous timeouted requests to be ignored
296 msgs := []mock.MsgWithContext{
297 {Msg: &vpe.ControlPingReply{}, SeqNum: 0xffff - 1},
298 {Msg: &vpe.ControlPingReply{}, SeqNum: 0xffff},
299 {Msg: &vpe.ControlPingReply{}, SeqNum: 0},
301 for i := 0; i < 10; i++ {
302 msgs = append(msgs, mock.MsgWithContext{
303 Msg: &interfaces.SwInterfaceDetails{SwIfIndex: uint32(i)},
308 // missing finalizing control ping
310 // reply for a next request
311 msgs = append(msgs, mock.MsgWithContext{
312 Msg: &vpe.ControlPingReply{},
317 ctx.mockVpp.MockReplyWithContext(msgs...)
319 // send multipart request
320 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
322 for i := 0; i < 10; i++ {
323 // receive multi-part replies
324 reply := &interfaces.SwInterfaceDetails{}
325 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
327 Expect(lastReplyReceived).To(BeFalse())
328 Expect(err).ShouldNot(HaveOccurred())
329 Expect(reply.SwIfIndex).To(BeEquivalentTo(i))
332 // missing closing control ping
333 reply := &interfaces.SwInterfaceDetails{}
334 _, err := reqCtx.ReceiveReply(reply)
335 Expect(err).ToNot(BeNil())
336 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
338 // try again - still fails and nothing consumed
339 _, err = reqCtx.ReceiveReply(reply)
340 Expect(err).ToNot(BeNil())
341 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
343 // reply for the second request has not been consumed
344 reqCtx2 := ctx.ch.SendRequest(&vpe.ControlPing{})
345 reply2 := &vpe.ControlPingReply{}
346 err = reqCtx2.ReceiveReply(reply2)
347 Expect(err).To(BeNil())
350 func TestRequestsOrdering(t *testing.T) {
351 ctx := setupTest(t, false)
352 defer ctx.teardownTest()
354 // the orderings of SendRequest and ReceiveReply calls should match, otherwise
355 // some replies will get thrown away
358 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
359 req1 := &vpe.ControlPing{}
360 reqCtx1 := ctx.ch.SendRequest(req1)
363 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
364 req2 := &vpe.ControlPing{}
365 reqCtx2 := ctx.ch.SendRequest(req2)
367 // if reply for the second request is read first, the reply for the first
368 // request gets thrown away.
369 reply2 := &vpe.ControlPingReply{}
370 err := reqCtx2.ReceiveReply(reply2)
371 Expect(err).To(BeNil())
373 // first request has already been considered closed
374 reply1 := &vpe.ControlPingReply{}
375 err = reqCtx1.ReceiveReply(reply1)
376 Expect(err).ToNot(BeNil())
377 Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
380 func TestCycleOverSetOfSequenceNumbers(t *testing.T) {
381 ctx := setupTest(t, false)
382 defer ctx.teardownTest()
384 numIters := 0xffff + 100
385 reqCtx := make(map[int]api.RequestCtx)
387 for i := 0; i < numIters+30; i++ {
389 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
390 req := &vpe.ControlPing{}
391 reqCtx[i] = ctx.ch.SendRequest(req)
394 reply := &vpe.ControlPingReply{}
395 err := reqCtx[i-30].ReceiveReply(reply)
396 Expect(err).ShouldNot(HaveOccurred())