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.
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/binapi/ethernet_types"
26 interfaces "git.fd.io/govpp.git/binapi/interface"
27 "git.fd.io/govpp.git/binapi/interface_types"
28 "git.fd.io/govpp.git/binapi/vpe"
29 "git.fd.io/govpp.git/codec"
30 "git.fd.io/govpp.git/core"
34 mockVpp *mock.VppAdapter
39 func setupTest(t *testing.T, bufferedChan bool) *testCtx {
43 mockVpp: mock.NewVppAdapter(),
47 ctx.conn, err = core.Connect(ctx.mockVpp)
48 Expect(err).ShouldNot(HaveOccurred())
51 ctx.ch, err = ctx.conn.NewAPIChannelBuffered(100, 100)
53 ctx.ch, err = ctx.conn.NewAPIChannel()
55 Expect(err).ShouldNot(HaveOccurred())
60 func (ctx *testCtx) teardownTest() {
65 func TestNilConnection(t *testing.T) {
67 var conn *core.Connection
69 ch, err := conn.NewAPIChannel()
70 Expect(ch).Should(BeNil())
71 Expect(err).Should(HaveOccurred())
72 Expect(err.Error()).To(ContainSubstring("nil"))
74 ch, err = conn.NewAPIChannelBuffered(1, 1)
75 Expect(ch).Should(BeNil())
76 Expect(err).Should(HaveOccurred())
77 Expect(err.Error()).To(ContainSubstring("nil"))
80 func TestAsyncConnection(t *testing.T) {
81 ctx := setupTest(t, false)
82 defer ctx.teardownTest()
85 conn, statusChan, err := core.AsyncConnect(ctx.mockVpp, core.DefaultMaxReconnectAttempts, core.DefaultReconnectInterval)
88 Expect(err).ShouldNot(HaveOccurred())
89 Expect(conn).ShouldNot(BeNil())
92 Expect(ev.State).Should(BeEquivalentTo(core.Connected))
95 func TestAsyncConnectionProcessesVppTimeout(t *testing.T) {
96 ctx := setupTest(t, false)
97 defer ctx.teardownTest()
100 conn, statusChan, err := core.AsyncConnect(ctx.mockVpp, core.DefaultMaxReconnectAttempts, core.DefaultReconnectInterval)
103 Expect(err).ShouldNot(HaveOccurred())
104 Expect(conn).ShouldNot(BeNil())
107 Expect(ev.State).Should(BeEquivalentTo(core.Connected))
109 // make control ping reply fail so that connection.healthCheckLoop()
110 // initiates reconnection.
111 ctx.mockVpp.MockReply(&vpe.ControlPingReply{
114 time.Sleep(time.Duration(1+core.HealthCheckThreshold) * (core.HealthCheckInterval + 2*core.HealthCheckReplyTimeout))
117 func TestCodec(t *testing.T) {
120 var msgCodec = codec.DefaultCodec
123 data, err := msgCodec.EncodeMsg(&interfaces.CreateLoopback{MacAddress: ethernet_types.MacAddress{1, 2, 3, 4, 5, 6}}, 11)
124 Expect(err).ShouldNot(HaveOccurred())
125 Expect(data).ShouldNot(BeEmpty())
127 msg1 := &interfaces.CreateLoopback{}
128 err = msgCodec.DecodeMsg(data, msg1)
129 Expect(err).ShouldNot(HaveOccurred())
130 Expect(msg1.MacAddress).To(BeEquivalentTo(ethernet_types.MacAddress{1, 2, 3, 4, 5, 6}))
133 data, err = msgCodec.EncodeMsg(&vpe.ControlPingReply{Retval: 55}, 22)
134 Expect(err).ShouldNot(HaveOccurred())
135 Expect(data).ShouldNot(BeEmpty())
137 msg2 := &vpe.ControlPingReply{}
138 err = msgCodec.DecodeMsg(data, msg2)
139 Expect(err).ShouldNot(HaveOccurred())
140 Expect(msg2.Retval).To(BeEquivalentTo(55))
143 func TestCodecNegative(t *testing.T) {
146 var msgCodec = codec.DefaultCodec
148 // nil message for encoding
149 data, err := msgCodec.EncodeMsg(nil, 15)
150 Expect(err).Should(HaveOccurred())
151 Expect(err.Error()).To(ContainSubstring("nil message"))
152 Expect(data).Should(BeNil())
154 // nil message for decoding
155 err = msgCodec.DecodeMsg(data, nil)
156 Expect(err).Should(HaveOccurred())
157 Expect(err.Error()).To(ContainSubstring("nil message"))
159 // nil data for decoding
160 err = msgCodec.DecodeMsg(nil, &vpe.ControlPingReply{})
161 Expect(err).Should(HaveOccurred())
162 Expect(err.Error()).To(ContainSubstring("panic"))
165 func TestSimpleRequestsWithSequenceNumbers(t *testing.T) {
166 ctx := setupTest(t, false)
167 defer ctx.teardownTest()
169 var reqCtx []api.RequestCtx
170 for i := 0; i < 10; i++ {
171 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
172 req := &vpe.ControlPing{}
173 reqCtx = append(reqCtx, ctx.ch.SendRequest(req))
176 for i := 0; i < 10; i++ {
177 reply := &vpe.ControlPingReply{}
178 err := reqCtx[i].ReceiveReply(reply)
179 Expect(err).ShouldNot(HaveOccurred())
183 func TestMultiRequestsWithSequenceNumbers(t *testing.T) {
184 ctx := setupTest(t, false)
185 defer ctx.teardownTest()
187 var msgs []api.Message
188 for i := 0; i < 10; i++ {
189 msgs = append(msgs, &interfaces.SwInterfaceDetails{SwIfIndex: interface_types.InterfaceIndex(i)})
191 ctx.mockVpp.MockReply(msgs...)
192 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
194 // send multipart request
195 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
199 Expect(cnt < 11).To(BeTrue())
202 reply := &interfaces.SwInterfaceDetails{}
203 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
205 if lastReplyReceived {
209 Expect(err).ShouldNot(HaveOccurred())
210 Expect(reply.SwIfIndex).To(BeEquivalentTo(cnt))
215 Expect(cnt).To(BeEquivalentTo(10))
218 func TestSimpleRequestWithTimeout(t *testing.T) {
219 ctx := setupTest(t, true)
220 defer ctx.teardownTest()
222 // reply for a previous timeouted requests to be ignored
223 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
224 Msg: &vpe.ControlPingReply{},
229 req1 := &vpe.ControlPing{}
230 reqCtx1 := ctx.ch.SendRequest(req1)
232 reply := &vpe.ControlPingReply{}
233 err := reqCtx1.ReceiveReply(reply)
234 Expect(err).ToNot(BeNil())
235 Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
237 ctx.mockVpp.MockReplyWithContext(
238 // reply for the previous request
240 Msg: &vpe.ControlPingReply{},
243 // reply for the next request
245 Msg: &vpe.ControlPingReply{},
250 req2 := &vpe.ControlPing{}
251 reqCtx2 := ctx.ch.SendRequest(req2)
253 // second request should ignore the first reply and return the second one
254 reply = &vpe.ControlPingReply{}
255 err = reqCtx2.ReceiveReply(reply)
256 Expect(err).To(BeNil())
259 func TestSimpleRequestsWithMissingReply(t *testing.T) {
260 ctx := setupTest(t, false)
261 defer ctx.teardownTest()
263 // request without reply
264 req1 := &vpe.ControlPing{}
265 reqCtx1 := ctx.ch.SendRequest(req1)
267 // another request without reply
268 req2 := &vpe.ControlPing{}
269 reqCtx2 := ctx.ch.SendRequest(req2)
271 // third request with reply
272 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
273 Msg: &vpe.ControlPingReply{},
276 req3 := &vpe.ControlPing{}
277 reqCtx3 := ctx.ch.SendRequest(req3)
279 // the first two should fail, but not consume reply for the 3rd
280 reply := &vpe.ControlPingReply{}
281 err := reqCtx1.ReceiveReply(reply)
282 Expect(err).ToNot(BeNil())
283 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
285 reply = &vpe.ControlPingReply{}
286 err = reqCtx2.ReceiveReply(reply)
287 Expect(err).ToNot(BeNil())
288 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 2"))
290 // the second request should succeed
291 reply = &vpe.ControlPingReply{}
292 err = reqCtx3.ReceiveReply(reply)
293 Expect(err).To(BeNil())
296 func TestMultiRequestsWithErrors(t *testing.T) {
297 ctx := setupTest(t, false)
298 defer ctx.teardownTest()
300 // replies for a previous timeouted requests to be ignored
301 msgs := []mock.MsgWithContext{
302 {Msg: &vpe.ControlPingReply{}, SeqNum: 0xffff - 1},
303 {Msg: &vpe.ControlPingReply{}, SeqNum: 0xffff},
304 {Msg: &vpe.ControlPingReply{}, SeqNum: 0},
306 for i := 0; i < 10; i++ {
307 msgs = append(msgs, mock.MsgWithContext{
308 Msg: &interfaces.SwInterfaceDetails{SwIfIndex: interface_types.InterfaceIndex(i)},
313 // missing finalizing control ping
315 // reply for a next request
316 msgs = append(msgs, mock.MsgWithContext{
317 Msg: &vpe.ControlPingReply{},
322 ctx.mockVpp.MockReplyWithContext(msgs...)
324 // send multipart request
325 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
327 for i := 0; i < 10; i++ {
328 // receive multi-part replies
329 reply := &interfaces.SwInterfaceDetails{}
330 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
332 Expect(lastReplyReceived).To(BeFalse())
333 Expect(err).ShouldNot(HaveOccurred())
334 Expect(reply.SwIfIndex).To(BeEquivalentTo(i))
337 // missing closing control ping
338 reply := &interfaces.SwInterfaceDetails{}
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 // try again - still fails and nothing consumed
344 _, err = reqCtx.ReceiveReply(reply)
345 Expect(err).ToNot(BeNil())
346 Expect(err.Error()).To(Equal("missing binary API reply with sequence number: 1"))
348 // reply for the second request has not been consumed
349 reqCtx2 := ctx.ch.SendRequest(&vpe.ControlPing{})
350 reply2 := &vpe.ControlPingReply{}
351 err = reqCtx2.ReceiveReply(reply2)
352 Expect(err).To(BeNil())
355 func TestRequestsOrdering(t *testing.T) {
356 ctx := setupTest(t, false)
357 defer ctx.teardownTest()
359 // the orderings of SendRequest and ReceiveReply calls should match, otherwise
360 // some replies will get thrown away
363 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
364 req1 := &vpe.ControlPing{}
365 reqCtx1 := ctx.ch.SendRequest(req1)
368 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
369 req2 := &vpe.ControlPing{}
370 reqCtx2 := ctx.ch.SendRequest(req2)
372 // if reply for the second request is read first, the reply for the first
373 // request gets thrown away.
374 reply2 := &vpe.ControlPingReply{}
375 err := reqCtx2.ReceiveReply(reply2)
376 Expect(err).To(BeNil())
378 // first request has already been considered closed
379 reply1 := &vpe.ControlPingReply{}
380 err = reqCtx1.ReceiveReply(reply1)
381 Expect(err).ToNot(BeNil())
382 Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
385 func TestCycleOverSetOfSequenceNumbers(t *testing.T) {
386 ctx := setupTest(t, false)
387 defer ctx.teardownTest()
389 numIters := 0xffff + 100
390 reqCtx := make(map[int]api.RequestCtx)
392 for i := 0; i < numIters+30; i++ {
394 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
395 req := &vpe.ControlPing{}
396 reqCtx[i] = ctx.ch.SendRequest(req)
399 reply := &vpe.ControlPingReply{}
400 err := reqCtx[i-30].ReceiveReply(reply)
401 Expect(err).ShouldNot(HaveOccurred())