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.
18 "git.fd.io/govpp.git/examples/binapi/interface_types"
21 . "github.com/onsi/gomega"
23 "git.fd.io/govpp.git/adapter/mock"
24 "git.fd.io/govpp.git/api"
25 "git.fd.io/govpp.git/codec"
26 "git.fd.io/govpp.git/core"
27 "git.fd.io/govpp.git/examples/binapi/interfaces"
28 "git.fd.io/govpp.git/examples/binapi/vpe"
32 mockVpp *mock.VppAdapter
37 func setupTest(t *testing.T, bufferedChan bool) *testCtx {
41 mockVpp: mock.NewVppAdapter(),
45 ctx.conn, err = core.Connect(ctx.mockVpp)
46 Expect(err).ShouldNot(HaveOccurred())
49 ctx.ch, err = ctx.conn.NewAPIChannelBuffered(100, 100)
51 ctx.ch, err = ctx.conn.NewAPIChannel()
53 Expect(err).ShouldNot(HaveOccurred())
58 func (ctx *testCtx) teardownTest() {
63 func TestNilConnection(t *testing.T) {
65 var conn *core.Connection
67 ch, err := conn.NewAPIChannel()
68 Expect(ch).Should(BeNil())
69 Expect(err).Should(HaveOccurred())
70 Expect(err.Error()).To(ContainSubstring("nil"))
72 ch, err = conn.NewAPIChannelBuffered(1, 1)
73 Expect(ch).Should(BeNil())
74 Expect(err).Should(HaveOccurred())
75 Expect(err.Error()).To(ContainSubstring("nil"))
78 func TestAsyncConnection(t *testing.T) {
79 ctx := setupTest(t, false)
80 defer ctx.teardownTest()
83 conn, statusChan, err := core.AsyncConnect(ctx.mockVpp, core.DefaultMaxReconnectAttempts, core.DefaultReconnectInterval)
86 Expect(err).ShouldNot(HaveOccurred())
87 Expect(conn).ShouldNot(BeNil())
90 Expect(ev.State).Should(BeEquivalentTo(core.Connected))
93 func TestCodec(t *testing.T) {
96 var msgCodec = codec.DefaultCodec
99 data, err := msgCodec.EncodeMsg(&interfaces.CreateLoopback{MacAddress: interfaces.MacAddress{1, 2, 3, 4, 5, 6}}, 11)
100 Expect(err).ShouldNot(HaveOccurred())
101 Expect(data).ShouldNot(BeEmpty())
103 msg1 := &interfaces.CreateLoopback{}
104 err = msgCodec.DecodeMsg(data, msg1)
105 Expect(err).ShouldNot(HaveOccurred())
106 Expect(msg1.MacAddress).To(BeEquivalentTo(interfaces.MacAddress{1, 2, 3, 4, 5, 6}))
109 data, err = msgCodec.EncodeMsg(&vpe.ControlPingReply{Retval: 55}, 22)
110 Expect(err).ShouldNot(HaveOccurred())
111 Expect(data).ShouldNot(BeEmpty())
113 msg2 := &vpe.ControlPingReply{}
114 err = msgCodec.DecodeMsg(data, msg2)
115 Expect(err).ShouldNot(HaveOccurred())
116 Expect(msg2.Retval).To(BeEquivalentTo(55))
119 func TestCodecNegative(t *testing.T) {
122 var msgCodec = codec.DefaultCodec
124 // nil message for encoding
125 data, err := msgCodec.EncodeMsg(nil, 15)
126 Expect(err).Should(HaveOccurred())
127 Expect(err.Error()).To(ContainSubstring("nil message"))
128 Expect(data).Should(BeNil())
130 // nil message for decoding
131 err = msgCodec.DecodeMsg(data, nil)
132 Expect(err).Should(HaveOccurred())
133 Expect(err.Error()).To(ContainSubstring("nil message"))
135 // nil data for decoding
136 err = msgCodec.DecodeMsg(nil, &vpe.ControlPingReply{})
137 Expect(err).Should(HaveOccurred())
138 Expect(err.Error()).To(ContainSubstring("panic"))
141 func TestSimpleRequestsWithSequenceNumbers(t *testing.T) {
142 ctx := setupTest(t, false)
143 defer ctx.teardownTest()
145 var reqCtx []api.RequestCtx
146 for i := 0; i < 10; i++ {
147 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
148 req := &vpe.ControlPing{}
149 reqCtx = append(reqCtx, ctx.ch.SendRequest(req))
152 for i := 0; i < 10; i++ {
153 reply := &vpe.ControlPingReply{}
154 err := reqCtx[i].ReceiveReply(reply)
155 Expect(err).ShouldNot(HaveOccurred())
159 func TestMultiRequestsWithSequenceNumbers(t *testing.T) {
160 ctx := setupTest(t, false)
161 defer ctx.teardownTest()
163 var msgs []api.Message
164 for i := 0; i < 10; i++ {
165 msgs = append(msgs, &interfaces.SwInterfaceDetails{SwIfIndex: interface_types.InterfaceIndex(i)})
167 ctx.mockVpp.MockReply(msgs...)
168 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
170 // send multipart request
171 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
175 Expect(cnt < 11).To(BeTrue())
178 reply := &interfaces.SwInterfaceDetails{}
179 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
181 if lastReplyReceived {
185 Expect(err).ShouldNot(HaveOccurred())
186 Expect(reply.SwIfIndex).To(BeEquivalentTo(cnt))
191 Expect(cnt).To(BeEquivalentTo(10))
194 func TestSimpleRequestWithTimeout(t *testing.T) {
195 ctx := setupTest(t, true)
196 defer ctx.teardownTest()
198 // reply for a previous timeouted requests to be ignored
199 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
200 Msg: &vpe.ControlPingReply{},
205 req1 := &vpe.ControlPing{}
206 reqCtx1 := ctx.ch.SendRequest(req1)
208 reply := &vpe.ControlPingReply{}
209 err := reqCtx1.ReceiveReply(reply)
210 Expect(err).ToNot(BeNil())
211 Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
213 ctx.mockVpp.MockReplyWithContext(
214 // reply for the previous request
216 Msg: &vpe.ControlPingReply{},
219 // reply for the next request
221 Msg: &vpe.ControlPingReply{},
226 req2 := &vpe.ControlPing{}
227 reqCtx2 := ctx.ch.SendRequest(req2)
229 // second request should ignore the first reply and return the second one
230 reply = &vpe.ControlPingReply{}
231 err = reqCtx2.ReceiveReply(reply)
232 Expect(err).To(BeNil())
235 func TestSimpleRequestsWithMissingReply(t *testing.T) {
236 ctx := setupTest(t, false)
237 defer ctx.teardownTest()
239 // request without reply
240 req1 := &vpe.ControlPing{}
241 reqCtx1 := ctx.ch.SendRequest(req1)
243 // another request without reply
244 req2 := &vpe.ControlPing{}
245 reqCtx2 := ctx.ch.SendRequest(req2)
247 // third request with reply
248 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
249 Msg: &vpe.ControlPingReply{},
252 req3 := &vpe.ControlPing{}
253 reqCtx3 := ctx.ch.SendRequest(req3)
255 // the first two should fail, but not consume reply for the 3rd
256 reply := &vpe.ControlPingReply{}
257 err := reqCtx1.ReceiveReply(reply)
258 Expect(err).ToNot(BeNil())
259 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
261 reply = &vpe.ControlPingReply{}
262 err = reqCtx2.ReceiveReply(reply)
263 Expect(err).ToNot(BeNil())
264 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 2"))
266 // the second request should succeed
267 reply = &vpe.ControlPingReply{}
268 err = reqCtx3.ReceiveReply(reply)
269 Expect(err).To(BeNil())
272 func TestMultiRequestsWithErrors(t *testing.T) {
273 ctx := setupTest(t, false)
274 defer ctx.teardownTest()
276 // replies for a previous timeouted requests to be ignored
277 msgs := []mock.MsgWithContext{
278 {Msg: &vpe.ControlPingReply{}, SeqNum: 0xffff - 1},
279 {Msg: &vpe.ControlPingReply{}, SeqNum: 0xffff},
280 {Msg: &vpe.ControlPingReply{}, SeqNum: 0},
282 for i := 0; i < 10; i++ {
283 msgs = append(msgs, mock.MsgWithContext{
284 Msg: &interfaces.SwInterfaceDetails{SwIfIndex: interface_types.InterfaceIndex(i)},
289 // missing finalizing control ping
291 // reply for a next request
292 msgs = append(msgs, mock.MsgWithContext{
293 Msg: &vpe.ControlPingReply{},
298 ctx.mockVpp.MockReplyWithContext(msgs...)
300 // send multipart request
301 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
303 for i := 0; i < 10; i++ {
304 // receive multi-part replies
305 reply := &interfaces.SwInterfaceDetails{}
306 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
308 Expect(lastReplyReceived).To(BeFalse())
309 Expect(err).ShouldNot(HaveOccurred())
310 Expect(reply.SwIfIndex).To(BeEquivalentTo(i))
313 // missing closing control ping
314 reply := &interfaces.SwInterfaceDetails{}
315 _, err := reqCtx.ReceiveReply(reply)
316 Expect(err).ToNot(BeNil())
317 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
319 // try again - still fails and nothing consumed
320 _, err = reqCtx.ReceiveReply(reply)
321 Expect(err).ToNot(BeNil())
322 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
324 // reply for the second request has not been consumed
325 reqCtx2 := ctx.ch.SendRequest(&vpe.ControlPing{})
326 reply2 := &vpe.ControlPingReply{}
327 err = reqCtx2.ReceiveReply(reply2)
328 Expect(err).To(BeNil())
331 func TestRequestsOrdering(t *testing.T) {
332 ctx := setupTest(t, false)
333 defer ctx.teardownTest()
335 // the orderings of SendRequest and ReceiveReply calls should match, otherwise
336 // some replies will get thrown away
339 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
340 req1 := &vpe.ControlPing{}
341 reqCtx1 := ctx.ch.SendRequest(req1)
344 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
345 req2 := &vpe.ControlPing{}
346 reqCtx2 := ctx.ch.SendRequest(req2)
348 // if reply for the second request is read first, the reply for the first
349 // request gets thrown away.
350 reply2 := &vpe.ControlPingReply{}
351 err := reqCtx2.ReceiveReply(reply2)
352 Expect(err).To(BeNil())
354 // first request has already been considered closed
355 reply1 := &vpe.ControlPingReply{}
356 err = reqCtx1.ReceiveReply(reply1)
357 Expect(err).ToNot(BeNil())
358 Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
361 func TestCycleOverSetOfSequenceNumbers(t *testing.T) {
362 ctx := setupTest(t, false)
363 defer ctx.teardownTest()
365 numIters := 0xffff + 100
366 reqCtx := make(map[int]api.RequestCtx)
368 for i := 0; i < numIters+30; i++ {
370 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
371 req := &vpe.ControlPing{}
372 reqCtx[i] = ctx.ch.SendRequest(req)
375 reply := &vpe.ControlPingReply{}
376 err := reqCtx[i-30].ReceiveReply(reply)
377 Expect(err).ShouldNot(HaveOccurred())