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/codec"
25 "git.fd.io/govpp.git/core"
26 "git.fd.io/govpp.git/examples/binapi/interfaces"
27 "git.fd.io/govpp.git/examples/binapi/vpe"
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 TestAsyncConnection(t *testing.T) {
78 ctx := setupTest(t, false)
79 defer ctx.teardownTest()
82 conn, statusChan, err := core.AsyncConnect(ctx.mockVpp, core.DefaultMaxReconnectAttempts, core.DefaultReconnectInterval)
85 Expect(err).ShouldNot(HaveOccurred())
86 Expect(conn).ShouldNot(BeNil())
89 Expect(ev.State).Should(BeEquivalentTo(core.Connected))
92 func TestCodec(t *testing.T) {
95 msgCodec := &codec.MsgCodec{}
98 data, err := msgCodec.EncodeMsg(&interfaces.CreateLoopback{MacAddress: interfaces.MacAddress{1, 2, 3, 4, 5, 6}}, 11)
99 Expect(err).ShouldNot(HaveOccurred())
100 Expect(data).ShouldNot(BeEmpty())
102 msg1 := &interfaces.CreateLoopback{}
103 err = msgCodec.DecodeMsg(data, msg1)
104 Expect(err).ShouldNot(HaveOccurred())
105 Expect(msg1.MacAddress).To(BeEquivalentTo(interfaces.MacAddress{1, 2, 3, 4, 5, 6}))
108 data, err = msgCodec.EncodeMsg(&vpe.ControlPingReply{Retval: 55}, 22)
109 Expect(err).ShouldNot(HaveOccurred())
110 Expect(data).ShouldNot(BeEmpty())
112 msg2 := &vpe.ControlPingReply{}
113 err = msgCodec.DecodeMsg(data, msg2)
114 Expect(err).ShouldNot(HaveOccurred())
115 Expect(msg2.Retval).To(BeEquivalentTo(55))
118 func TestCodecNegative(t *testing.T) {
121 msgCodec := &codec.MsgCodec{}
123 // nil message for encoding
124 data, err := msgCodec.EncodeMsg(nil, 15)
125 Expect(err).Should(HaveOccurred())
126 Expect(err.Error()).To(ContainSubstring("nil message"))
127 Expect(data).Should(BeNil())
129 // nil message for decoding
130 err = msgCodec.DecodeMsg(data, nil)
131 Expect(err).Should(HaveOccurred())
132 Expect(err.Error()).To(ContainSubstring("nil message"))
134 // nil data for decoding
135 err = msgCodec.DecodeMsg(nil, &vpe.ControlPingReply{})
136 Expect(err).Should(HaveOccurred())
137 Expect(err.Error()).To(ContainSubstring("EOF"))
140 func TestSimpleRequestsWithSequenceNumbers(t *testing.T) {
141 ctx := setupTest(t, false)
142 defer ctx.teardownTest()
144 var reqCtx []api.RequestCtx
145 for i := 0; i < 10; i++ {
146 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
147 req := &vpe.ControlPing{}
148 reqCtx = append(reqCtx, ctx.ch.SendRequest(req))
151 for i := 0; i < 10; i++ {
152 reply := &vpe.ControlPingReply{}
153 err := reqCtx[i].ReceiveReply(reply)
154 Expect(err).ShouldNot(HaveOccurred())
158 func TestMultiRequestsWithSequenceNumbers(t *testing.T) {
159 ctx := setupTest(t, false)
160 defer ctx.teardownTest()
162 var msgs []api.Message
163 for i := 0; i < 10; i++ {
164 msgs = append(msgs, &interfaces.SwInterfaceDetails{SwIfIndex: interfaces.InterfaceIndex(i)})
166 ctx.mockVpp.MockReply(msgs...)
167 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
169 // send multipart request
170 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
174 Expect(cnt < 11).To(BeTrue())
177 reply := &interfaces.SwInterfaceDetails{}
178 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
180 if lastReplyReceived {
184 Expect(err).ShouldNot(HaveOccurred())
185 Expect(reply.SwIfIndex).To(BeEquivalentTo(cnt))
190 Expect(cnt).To(BeEquivalentTo(10))
193 func TestSimpleRequestWithTimeout(t *testing.T) {
194 ctx := setupTest(t, true)
195 defer ctx.teardownTest()
197 // reply for a previous timeouted requests to be ignored
198 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
199 Msg: &vpe.ControlPingReply{},
204 req1 := &vpe.ControlPing{}
205 reqCtx1 := ctx.ch.SendRequest(req1)
207 reply := &vpe.ControlPingReply{}
208 err := reqCtx1.ReceiveReply(reply)
209 Expect(err).ToNot(BeNil())
210 Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
212 ctx.mockVpp.MockReplyWithContext(
213 // reply for the previous request
215 Msg: &vpe.ControlPingReply{},
218 // reply for the next request
220 Msg: &vpe.ControlPingReply{},
225 req2 := &vpe.ControlPing{}
226 reqCtx2 := ctx.ch.SendRequest(req2)
228 // second request should ignore the first reply and return the second one
229 reply = &vpe.ControlPingReply{}
230 err = reqCtx2.ReceiveReply(reply)
231 Expect(err).To(BeNil())
234 func TestSimpleRequestsWithMissingReply(t *testing.T) {
235 ctx := setupTest(t, false)
236 defer ctx.teardownTest()
238 // request without reply
239 req1 := &vpe.ControlPing{}
240 reqCtx1 := ctx.ch.SendRequest(req1)
242 // another request without reply
243 req2 := &vpe.ControlPing{}
244 reqCtx2 := ctx.ch.SendRequest(req2)
246 // third request with reply
247 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
248 Msg: &vpe.ControlPingReply{},
251 req3 := &vpe.ControlPing{}
252 reqCtx3 := ctx.ch.SendRequest(req3)
254 // the first two should fail, but not consume reply for the 3rd
255 reply := &vpe.ControlPingReply{}
256 err := reqCtx1.ReceiveReply(reply)
257 Expect(err).ToNot(BeNil())
258 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
260 reply = &vpe.ControlPingReply{}
261 err = reqCtx2.ReceiveReply(reply)
262 Expect(err).ToNot(BeNil())
263 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 2"))
265 // the second request should succeed
266 reply = &vpe.ControlPingReply{}
267 err = reqCtx3.ReceiveReply(reply)
268 Expect(err).To(BeNil())
271 func TestMultiRequestsWithErrors(t *testing.T) {
272 ctx := setupTest(t, false)
273 defer ctx.teardownTest()
275 // replies for a previous timeouted requests to be ignored
276 msgs := []mock.MsgWithContext{
277 {Msg: &vpe.ControlPingReply{}, SeqNum: 0xffff - 1},
278 {Msg: &vpe.ControlPingReply{}, SeqNum: 0xffff},
279 {Msg: &vpe.ControlPingReply{}, SeqNum: 0},
281 for i := 0; i < 10; i++ {
282 msgs = append(msgs, mock.MsgWithContext{
283 Msg: &interfaces.SwInterfaceDetails{SwIfIndex: interfaces.InterfaceIndex(i)},
288 // missing finalizing control ping
290 // reply for a next request
291 msgs = append(msgs, mock.MsgWithContext{
292 Msg: &vpe.ControlPingReply{},
297 ctx.mockVpp.MockReplyWithContext(msgs...)
299 // send multipart request
300 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
302 for i := 0; i < 10; i++ {
303 // receive multi-part replies
304 reply := &interfaces.SwInterfaceDetails{}
305 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
307 Expect(lastReplyReceived).To(BeFalse())
308 Expect(err).ShouldNot(HaveOccurred())
309 Expect(reply.SwIfIndex).To(BeEquivalentTo(i))
312 // missing closing control ping
313 reply := &interfaces.SwInterfaceDetails{}
314 _, err := reqCtx.ReceiveReply(reply)
315 Expect(err).ToNot(BeNil())
316 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
318 // try again - still fails and nothing consumed
319 _, err = reqCtx.ReceiveReply(reply)
320 Expect(err).ToNot(BeNil())
321 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
323 // reply for the second request has not been consumed
324 reqCtx2 := ctx.ch.SendRequest(&vpe.ControlPing{})
325 reply2 := &vpe.ControlPingReply{}
326 err = reqCtx2.ReceiveReply(reply2)
327 Expect(err).To(BeNil())
330 func TestRequestsOrdering(t *testing.T) {
331 ctx := setupTest(t, false)
332 defer ctx.teardownTest()
334 // the orderings of SendRequest and ReceiveReply calls should match, otherwise
335 // some replies will get thrown away
338 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
339 req1 := &vpe.ControlPing{}
340 reqCtx1 := ctx.ch.SendRequest(req1)
343 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
344 req2 := &vpe.ControlPing{}
345 reqCtx2 := ctx.ch.SendRequest(req2)
347 // if reply for the second request is read first, the reply for the first
348 // request gets thrown away.
349 reply2 := &vpe.ControlPingReply{}
350 err := reqCtx2.ReceiveReply(reply2)
351 Expect(err).To(BeNil())
353 // first request has already been considered closed
354 reply1 := &vpe.ControlPingReply{}
355 err = reqCtx1.ReceiveReply(reply1)
356 Expect(err).ToNot(BeNil())
357 Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
360 func TestCycleOverSetOfSequenceNumbers(t *testing.T) {
361 ctx := setupTest(t, false)
362 defer ctx.teardownTest()
364 numIters := 0xffff + 100
365 reqCtx := make(map[int]api.RequestCtx)
367 for i := 0; i < numIters+30; i++ {
369 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
370 req := &vpe.ControlPing{}
371 reqCtx[i] = ctx.ch.SendRequest(req)
374 reply := &vpe.ControlPingReply{}
375 err := reqCtx[i-30].ReceiveReply(reply)
376 Expect(err).ShouldNot(HaveOccurred())