Merge "Add .gitreview"
[govpp.git] / core / connection_test.go
1 // Copyright (c) 2017 Cisco and/or its affiliates.
2 //
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:
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
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.
14
15 package core_test
16
17 import (
18         "testing"
19
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/stats"
26         "git.fd.io/govpp.git/examples/bin_api/vpe"
27         . "github.com/onsi/gomega"
28 )
29
30 type testCtx struct {
31         mockVpp *mock.VppAdapter
32         conn    *core.Connection
33         ch      api.Channel
34 }
35
36 func setupTest(t *testing.T, bufferedChan bool) *testCtx {
37         RegisterTestingT(t)
38
39         ctx := &testCtx{
40                 mockVpp: mock.NewVppAdapter(),
41         }
42
43         var err error
44         ctx.conn, err = core.Connect(ctx.mockVpp)
45         Expect(err).ShouldNot(HaveOccurred())
46
47         if bufferedChan {
48                 ctx.ch, err = ctx.conn.NewAPIChannelBuffered(100, 100)
49         } else {
50                 ctx.ch, err = ctx.conn.NewAPIChannel()
51         }
52         Expect(err).ShouldNot(HaveOccurred())
53
54         return ctx
55 }
56
57 func (ctx *testCtx) teardownTest() {
58         ctx.ch.Close()
59         ctx.conn.Disconnect()
60 }
61
62 func TestNilConnection(t *testing.T) {
63         RegisterTestingT(t)
64         var conn *core.Connection
65
66         ch, err := conn.NewAPIChannel()
67         Expect(ch).Should(BeNil())
68         Expect(err).Should(HaveOccurred())
69         Expect(err.Error()).To(ContainSubstring("nil"))
70
71         ch, err = conn.NewAPIChannelBuffered(1, 1)
72         Expect(ch).Should(BeNil())
73         Expect(err).Should(HaveOccurred())
74         Expect(err.Error()).To(ContainSubstring("nil"))
75 }
76
77 func TestDoubleConnection(t *testing.T) {
78         ctx := setupTest(t, false)
79         defer ctx.teardownTest()
80
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())
85 }
86
87 func TestAsyncConnection(t *testing.T) {
88         ctx := setupTest(t, false)
89         defer ctx.teardownTest()
90
91         ctx.conn.Disconnect()
92         conn, statusChan, err := core.AsyncConnect(ctx.mockVpp)
93         ctx.conn = conn
94
95         Expect(err).ShouldNot(HaveOccurred())
96         Expect(conn).ShouldNot(BeNil())
97
98         ev := <-statusChan
99         Expect(ev.State).Should(BeEquivalentTo(core.Connected))
100 }
101
102 func TestCodec(t *testing.T) {
103         RegisterTestingT(t)
104
105         msgCodec := &codec.MsgCodec{}
106
107         // request
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())
111
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}))
116
117         // reply
118         data, err = msgCodec.EncodeMsg(&vpe.ControlPingReply{Retval: 55}, 22)
119         Expect(err).ShouldNot(HaveOccurred())
120         Expect(data).ShouldNot(BeEmpty())
121
122         msg2 := &vpe.ControlPingReply{}
123         err = msgCodec.DecodeMsg(data, msg2)
124         Expect(err).ShouldNot(HaveOccurred())
125         Expect(msg2.Retval).To(BeEquivalentTo(55))
126
127         // other
128         data, err = msgCodec.EncodeMsg(&stats.VnetIP4FibCounters{VrfID: 77}, 33)
129         Expect(err).ShouldNot(HaveOccurred())
130         Expect(data).ShouldNot(BeEmpty())
131
132         msg3 := &stats.VnetIP4FibCounters{}
133         err = msgCodec.DecodeMsg(data, msg3)
134         Expect(err).ShouldNot(HaveOccurred())
135         Expect(msg3.VrfID).To(BeEquivalentTo(77))
136 }
137
138 func TestCodecNegative(t *testing.T) {
139         RegisterTestingT(t)
140
141         msgCodec := &codec.MsgCodec{}
142
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())
148
149         // nil message for decoding
150         err = msgCodec.DecodeMsg(data, nil)
151         Expect(err).Should(HaveOccurred())
152         Expect(err.Error()).To(ContainSubstring("nil message"))
153
154         // nil data for decoding
155         err = msgCodec.DecodeMsg(nil, &vpe.ControlPingReply{})
156         Expect(err).Should(HaveOccurred())
157         Expect(err.Error()).To(ContainSubstring("EOF"))
158 }
159
160 func TestSimpleRequestsWithSequenceNumbers(t *testing.T) {
161         ctx := setupTest(t, false)
162         defer ctx.teardownTest()
163
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))
169         }
170
171         for i := 0; i < 10; i++ {
172                 reply := &vpe.ControlPingReply{}
173                 err := reqCtx[i].ReceiveReply(reply)
174                 Expect(err).ShouldNot(HaveOccurred())
175         }
176 }
177
178 func TestMultiRequestsWithSequenceNumbers(t *testing.T) {
179         ctx := setupTest(t, false)
180         defer ctx.teardownTest()
181
182         var msgs []api.Message
183         for i := 0; i < 10; i++ {
184                 msgs = append(msgs, &interfaces.SwInterfaceDetails{SwIfIndex: uint32(i)})
185         }
186         ctx.mockVpp.MockReply(msgs...)
187         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
188
189         // send multipart request
190         reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
191
192         cnt := 0
193         for {
194                 Expect(cnt < 11).To(BeTrue())
195
196                 // receive a reply
197                 reply := &interfaces.SwInterfaceDetails{}
198                 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
199
200                 if lastReplyReceived {
201                         break
202                 }
203
204                 Expect(err).ShouldNot(HaveOccurred())
205                 Expect(reply.SwIfIndex).To(BeEquivalentTo(cnt))
206
207                 cnt++
208         }
209
210         Expect(cnt).To(BeEquivalentTo(10))
211 }
212
213 func TestSimpleRequestWithTimeout(t *testing.T) {
214         ctx := setupTest(t, true)
215         defer ctx.teardownTest()
216
217         // reply for a previous timeouted requests to be ignored
218         ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
219                 Msg:    &vpe.ControlPingReply{},
220                 SeqNum: 0,
221         })
222
223         // send reply later
224         req1 := &vpe.ControlPing{}
225         reqCtx1 := ctx.ch.SendRequest(req1)
226
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"))
231
232         ctx.mockVpp.MockReplyWithContext(
233                 // reply for the previous request
234                 mock.MsgWithContext{
235                         Msg:    &vpe.ControlPingReply{},
236                         SeqNum: 1,
237                 },
238                 // reply for the next request
239                 mock.MsgWithContext{
240                         Msg:    &vpe.ControlPingReply{},
241                         SeqNum: 2,
242                 })
243
244         // next request
245         req2 := &vpe.ControlPing{}
246         reqCtx2 := ctx.ch.SendRequest(req2)
247
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())
252 }
253
254 func TestSimpleRequestsWithMissingReply(t *testing.T) {
255         ctx := setupTest(t, false)
256         defer ctx.teardownTest()
257
258         // request without reply
259         req1 := &vpe.ControlPing{}
260         reqCtx1 := ctx.ch.SendRequest(req1)
261
262         // another request without reply
263         req2 := &vpe.ControlPing{}
264         reqCtx2 := ctx.ch.SendRequest(req2)
265
266         // third request with reply
267         ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
268                 Msg:    &vpe.ControlPingReply{},
269                 SeqNum: 3,
270         })
271         req3 := &vpe.ControlPing{}
272         reqCtx3 := ctx.ch.SendRequest(req3)
273
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"))
279
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"))
284
285         // the second request should succeed
286         reply = &vpe.ControlPingReply{}
287         err = reqCtx3.ReceiveReply(reply)
288         Expect(err).To(BeNil())
289 }
290
291 func TestMultiRequestsWithErrors(t *testing.T) {
292         ctx := setupTest(t, false)
293         defer ctx.teardownTest()
294
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},
300         }
301         for i := 0; i < 10; i++ {
302                 msgs = append(msgs, mock.MsgWithContext{
303                         Msg:       &interfaces.SwInterfaceDetails{SwIfIndex: uint32(i)},
304                         SeqNum:    1,
305                         Multipart: true,
306                 })
307         }
308         // missing finalizing control ping
309
310         // reply for a next request
311         msgs = append(msgs, mock.MsgWithContext{
312                 Msg:    &vpe.ControlPingReply{},
313                 SeqNum: 2,
314         })
315
316         // queue replies
317         ctx.mockVpp.MockReplyWithContext(msgs...)
318
319         // send multipart request
320         reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
321
322         for i := 0; i < 10; i++ {
323                 // receive multi-part replies
324                 reply := &interfaces.SwInterfaceDetails{}
325                 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
326
327                 Expect(lastReplyReceived).To(BeFalse())
328                 Expect(err).ShouldNot(HaveOccurred())
329                 Expect(reply.SwIfIndex).To(BeEquivalentTo(i))
330         }
331
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"))
337
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"))
342
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())
348 }
349
350 func TestRequestsOrdering(t *testing.T) {
351         ctx := setupTest(t, false)
352         defer ctx.teardownTest()
353
354         // the orderings of SendRequest and ReceiveReply calls should match, otherwise
355         // some replies will get thrown away
356
357         // first request
358         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
359         req1 := &vpe.ControlPing{}
360         reqCtx1 := ctx.ch.SendRequest(req1)
361
362         // second request
363         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
364         req2 := &vpe.ControlPing{}
365         reqCtx2 := ctx.ch.SendRequest(req2)
366
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())
372
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"))
378 }
379
380 func TestCycleOverSetOfSequenceNumbers(t *testing.T) {
381         ctx := setupTest(t, false)
382         defer ctx.teardownTest()
383
384         numIters := 0xffff + 100
385         reqCtx := make(map[int]api.RequestCtx)
386
387         for i := 0; i < numIters+30; i++ {
388                 if i < numIters {
389                         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
390                         req := &vpe.ControlPing{}
391                         reqCtx[i] = ctx.ch.SendRequest(req)
392                 }
393                 if i > 30 {
394                         reply := &vpe.ControlPingReply{}
395                         err := reqCtx[i-30].ReceiveReply(reply)
396                         Expect(err).ShouldNot(HaveOccurred())
397                 }
398         }
399 }