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/bin_api/vpe"
26 . "github.com/onsi/gomega"
30 mockVpp *mock.VppAdapter
35 func setupTest(t *testing.T, bufferedChan bool) *testCtx {
39 mockVpp: mock.NewVppAdapter(),
43 ctx.conn, err = core.Connect(ctx.mockVpp)
44 Expect(err).ShouldNot(HaveOccurred())
47 ctx.ch, err = ctx.conn.NewAPIChannelBuffered(100, 100)
49 ctx.ch, err = ctx.conn.NewAPIChannel()
51 Expect(err).ShouldNot(HaveOccurred())
56 func (ctx *testCtx) teardownTest() {
61 func TestNilConnection(t *testing.T) {
63 var conn *core.Connection
65 ch, err := conn.NewAPIChannel()
66 Expect(ch).Should(BeNil())
67 Expect(err).Should(HaveOccurred())
68 Expect(err.Error()).To(ContainSubstring("nil"))
70 ch, err = conn.NewAPIChannelBuffered(1, 1)
71 Expect(ch).Should(BeNil())
72 Expect(err).Should(HaveOccurred())
73 Expect(err.Error()).To(ContainSubstring("nil"))
76 func TestAsyncConnection(t *testing.T) {
77 ctx := setupTest(t, false)
78 defer ctx.teardownTest()
81 conn, statusChan, err := core.AsyncConnect(ctx.mockVpp, core.DefaultMaxReconnectAttempts, core.DefaultReconnectInterval)
84 Expect(err).ShouldNot(HaveOccurred())
85 Expect(conn).ShouldNot(BeNil())
88 Expect(ev.State).Should(BeEquivalentTo(core.Connected))
91 func TestCodec(t *testing.T) {
94 msgCodec := &codec.MsgCodec{}
97 data, err := msgCodec.EncodeMsg(&interfaces.CreateLoopback{MacAddress: []byte{1, 2, 3, 4, 5, 6}}, 11)
98 Expect(err).ShouldNot(HaveOccurred())
99 Expect(data).ShouldNot(BeEmpty())
101 msg1 := &interfaces.CreateLoopback{}
102 err = msgCodec.DecodeMsg(data, msg1)
103 Expect(err).ShouldNot(HaveOccurred())
104 Expect(msg1.MacAddress).To(BeEquivalentTo([]byte{1, 2, 3, 4, 5, 6}))
107 data, err = msgCodec.EncodeMsg(&vpe.ControlPingReply{Retval: 55}, 22)
108 Expect(err).ShouldNot(HaveOccurred())
109 Expect(data).ShouldNot(BeEmpty())
111 msg2 := &vpe.ControlPingReply{}
112 err = msgCodec.DecodeMsg(data, msg2)
113 Expect(err).ShouldNot(HaveOccurred())
114 Expect(msg2.Retval).To(BeEquivalentTo(55))
117 func TestCodecNegative(t *testing.T) {
120 msgCodec := &codec.MsgCodec{}
122 // nil message for encoding
123 data, err := msgCodec.EncodeMsg(nil, 15)
124 Expect(err).Should(HaveOccurred())
125 Expect(err.Error()).To(ContainSubstring("nil message"))
126 Expect(data).Should(BeNil())
128 // nil message for decoding
129 err = msgCodec.DecodeMsg(data, nil)
130 Expect(err).Should(HaveOccurred())
131 Expect(err.Error()).To(ContainSubstring("nil message"))
133 // nil data for decoding
134 err = msgCodec.DecodeMsg(nil, &vpe.ControlPingReply{})
135 Expect(err).Should(HaveOccurred())
136 Expect(err.Error()).To(ContainSubstring("EOF"))
139 func TestSimpleRequestsWithSequenceNumbers(t *testing.T) {
140 ctx := setupTest(t, false)
141 defer ctx.teardownTest()
143 var reqCtx []api.RequestCtx
144 for i := 0; i < 10; i++ {
145 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
146 req := &vpe.ControlPing{}
147 reqCtx = append(reqCtx, ctx.ch.SendRequest(req))
150 for i := 0; i < 10; i++ {
151 reply := &vpe.ControlPingReply{}
152 err := reqCtx[i].ReceiveReply(reply)
153 Expect(err).ShouldNot(HaveOccurred())
157 func TestMultiRequestsWithSequenceNumbers(t *testing.T) {
158 ctx := setupTest(t, false)
159 defer ctx.teardownTest()
161 var msgs []api.Message
162 for i := 0; i < 10; i++ {
163 msgs = append(msgs, &interfaces.SwInterfaceDetails{SwIfIndex: uint32(i)})
165 ctx.mockVpp.MockReply(msgs...)
166 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
168 // send multipart request
169 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
173 Expect(cnt < 11).To(BeTrue())
176 reply := &interfaces.SwInterfaceDetails{}
177 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
179 if lastReplyReceived {
183 Expect(err).ShouldNot(HaveOccurred())
184 Expect(reply.SwIfIndex).To(BeEquivalentTo(cnt))
189 Expect(cnt).To(BeEquivalentTo(10))
192 func TestSimpleRequestWithTimeout(t *testing.T) {
193 ctx := setupTest(t, true)
194 defer ctx.teardownTest()
196 // reply for a previous timeouted requests to be ignored
197 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
198 Msg: &vpe.ControlPingReply{},
203 req1 := &vpe.ControlPing{}
204 reqCtx1 := ctx.ch.SendRequest(req1)
206 reply := &vpe.ControlPingReply{}
207 err := reqCtx1.ReceiveReply(reply)
208 Expect(err).ToNot(BeNil())
209 Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
211 ctx.mockVpp.MockReplyWithContext(
212 // reply for the previous request
214 Msg: &vpe.ControlPingReply{},
217 // reply for the next request
219 Msg: &vpe.ControlPingReply{},
224 req2 := &vpe.ControlPing{}
225 reqCtx2 := ctx.ch.SendRequest(req2)
227 // second request should ignore the first reply and return the second one
228 reply = &vpe.ControlPingReply{}
229 err = reqCtx2.ReceiveReply(reply)
230 Expect(err).To(BeNil())
233 func TestSimpleRequestsWithMissingReply(t *testing.T) {
234 ctx := setupTest(t, false)
235 defer ctx.teardownTest()
237 // request without reply
238 req1 := &vpe.ControlPing{}
239 reqCtx1 := ctx.ch.SendRequest(req1)
241 // another request without reply
242 req2 := &vpe.ControlPing{}
243 reqCtx2 := ctx.ch.SendRequest(req2)
245 // third request with reply
246 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
247 Msg: &vpe.ControlPingReply{},
250 req3 := &vpe.ControlPing{}
251 reqCtx3 := ctx.ch.SendRequest(req3)
253 // the first two should fail, but not consume reply for the 3rd
254 reply := &vpe.ControlPingReply{}
255 err := reqCtx1.ReceiveReply(reply)
256 Expect(err).ToNot(BeNil())
257 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
259 reply = &vpe.ControlPingReply{}
260 err = reqCtx2.ReceiveReply(reply)
261 Expect(err).ToNot(BeNil())
262 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 2"))
264 // the second request should succeed
265 reply = &vpe.ControlPingReply{}
266 err = reqCtx3.ReceiveReply(reply)
267 Expect(err).To(BeNil())
270 func TestMultiRequestsWithErrors(t *testing.T) {
271 ctx := setupTest(t, false)
272 defer ctx.teardownTest()
274 // replies for a previous timeouted requests to be ignored
275 msgs := []mock.MsgWithContext{
276 {Msg: &vpe.ControlPingReply{}, SeqNum: 0xffff - 1},
277 {Msg: &vpe.ControlPingReply{}, SeqNum: 0xffff},
278 {Msg: &vpe.ControlPingReply{}, SeqNum: 0},
280 for i := 0; i < 10; i++ {
281 msgs = append(msgs, mock.MsgWithContext{
282 Msg: &interfaces.SwInterfaceDetails{SwIfIndex: uint32(i)},
287 // missing finalizing control ping
289 // reply for a next request
290 msgs = append(msgs, mock.MsgWithContext{
291 Msg: &vpe.ControlPingReply{},
296 ctx.mockVpp.MockReplyWithContext(msgs...)
298 // send multipart request
299 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
301 for i := 0; i < 10; i++ {
302 // receive multi-part replies
303 reply := &interfaces.SwInterfaceDetails{}
304 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
306 Expect(lastReplyReceived).To(BeFalse())
307 Expect(err).ShouldNot(HaveOccurred())
308 Expect(reply.SwIfIndex).To(BeEquivalentTo(i))
311 // missing closing control ping
312 reply := &interfaces.SwInterfaceDetails{}
313 _, err := reqCtx.ReceiveReply(reply)
314 Expect(err).ToNot(BeNil())
315 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
317 // try again - still fails and nothing consumed
318 _, err = reqCtx.ReceiveReply(reply)
319 Expect(err).ToNot(BeNil())
320 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
322 // reply for the second request has not been consumed
323 reqCtx2 := ctx.ch.SendRequest(&vpe.ControlPing{})
324 reply2 := &vpe.ControlPingReply{}
325 err = reqCtx2.ReceiveReply(reply2)
326 Expect(err).To(BeNil())
329 func TestRequestsOrdering(t *testing.T) {
330 ctx := setupTest(t, false)
331 defer ctx.teardownTest()
333 // the orderings of SendRequest and ReceiveReply calls should match, otherwise
334 // some replies will get thrown away
337 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
338 req1 := &vpe.ControlPing{}
339 reqCtx1 := ctx.ch.SendRequest(req1)
342 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
343 req2 := &vpe.ControlPing{}
344 reqCtx2 := ctx.ch.SendRequest(req2)
346 // if reply for the second request is read first, the reply for the first
347 // request gets thrown away.
348 reply2 := &vpe.ControlPingReply{}
349 err := reqCtx2.ReceiveReply(reply2)
350 Expect(err).To(BeNil())
352 // first request has already been considered closed
353 reply1 := &vpe.ControlPingReply{}
354 err = reqCtx1.ReceiveReply(reply1)
355 Expect(err).ToNot(BeNil())
356 Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
359 func TestCycleOverSetOfSequenceNumbers(t *testing.T) {
360 ctx := setupTest(t, false)
361 defer ctx.teardownTest()
363 numIters := 0xffff + 100
364 reqCtx := make(map[int]api.RequestCtx)
366 for i := 0; i < numIters+30; i++ {
368 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
369 req := &vpe.ControlPing{}
370 reqCtx[i] = ctx.ch.SendRequest(req)
373 reply := &vpe.ControlPingReply{}
374 err := reqCtx[i-30].ReceiveReply(reply)
375 Expect(err).ShouldNot(HaveOccurred())