d2f0590510e079ab45db01dbf5f03afc0a500533
[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         "time"
20
21         . "github.com/onsi/gomega"
22
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         memclnt "git.fd.io/govpp.git/binapi/memclnt"
29         "git.fd.io/govpp.git/codec"
30         "git.fd.io/govpp.git/core"
31 )
32
33 type testCtx struct {
34         mockVpp *mock.VppAdapter
35         conn    *core.Connection
36         ch      api.Channel
37 }
38
39 func setupTest(t *testing.T, bufferedChan bool) *testCtx {
40         RegisterTestingT(t)
41
42         ctx := &testCtx{
43                 mockVpp: mock.NewVppAdapter(),
44         }
45
46         var err error
47         ctx.conn, err = core.Connect(ctx.mockVpp)
48         Expect(err).ShouldNot(HaveOccurred())
49
50         if bufferedChan {
51                 ctx.ch, err = ctx.conn.NewAPIChannelBuffered(100, 100)
52         } else {
53                 ctx.ch, err = ctx.conn.NewAPIChannel()
54         }
55         Expect(err).ShouldNot(HaveOccurred())
56
57         return ctx
58 }
59
60 func (ctx *testCtx) teardownTest() {
61         ctx.ch.Close()
62         ctx.conn.Disconnect()
63 }
64
65 func TestNilConnection(t *testing.T) {
66         RegisterTestingT(t)
67         var conn *core.Connection
68
69         ch, err := conn.NewAPIChannel()
70         Expect(ch).Should(BeNil())
71         Expect(err).Should(HaveOccurred())
72         Expect(err.Error()).To(ContainSubstring("nil"))
73
74         ch, err = conn.NewAPIChannelBuffered(1, 1)
75         Expect(ch).Should(BeNil())
76         Expect(err).Should(HaveOccurred())
77         Expect(err.Error()).To(ContainSubstring("nil"))
78 }
79
80 func TestAsyncConnection(t *testing.T) {
81         ctx := setupTest(t, false)
82         defer ctx.teardownTest()
83
84         ctx.conn.Disconnect()
85         conn, statusChan, err := core.AsyncConnect(ctx.mockVpp, core.DefaultMaxReconnectAttempts, core.DefaultReconnectInterval)
86         ctx.conn = conn
87
88         Expect(err).ShouldNot(HaveOccurred())
89         Expect(conn).ShouldNot(BeNil())
90
91         ev := <-statusChan
92         Expect(ev.State).Should(BeEquivalentTo(core.Connected))
93 }
94
95 func TestAsyncConnectionProcessesVppTimeout(t *testing.T) {
96         ctx := setupTest(t, false)
97         defer ctx.teardownTest()
98
99         ctx.conn.Disconnect()
100         conn, statusChan, err := core.AsyncConnect(ctx.mockVpp, core.DefaultMaxReconnectAttempts, core.DefaultReconnectInterval)
101         ctx.conn = conn
102
103         Expect(err).ShouldNot(HaveOccurred())
104         Expect(conn).ShouldNot(BeNil())
105
106         ev := <-statusChan
107         Expect(ev.State).Should(BeEquivalentTo(core.Connected))
108
109         // make control ping reply fail so that connection.healthCheckLoop()
110         // initiates reconnection.
111         ctx.mockVpp.MockReply(&memclnt.ControlPingReply{
112                 Retval: -1,
113         })
114         time.Sleep(time.Duration(1+core.HealthCheckThreshold) * (core.HealthCheckInterval + 2*core.HealthCheckReplyTimeout))
115 }
116
117 func TestCodec(t *testing.T) {
118         RegisterTestingT(t)
119
120         var msgCodec = codec.DefaultCodec
121
122         // request
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())
126
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}))
131
132         // reply
133         data, err = msgCodec.EncodeMsg(&memclnt.ControlPingReply{Retval: 55}, 22)
134         Expect(err).ShouldNot(HaveOccurred())
135         Expect(data).ShouldNot(BeEmpty())
136
137         msg2 := &memclnt.ControlPingReply{}
138         err = msgCodec.DecodeMsg(data, msg2)
139         Expect(err).ShouldNot(HaveOccurred())
140         Expect(msg2.Retval).To(BeEquivalentTo(55))
141 }
142
143 func TestCodecNegative(t *testing.T) {
144         RegisterTestingT(t)
145
146         var msgCodec = codec.DefaultCodec
147
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())
153
154         // nil message for decoding
155         err = msgCodec.DecodeMsg(data, nil)
156         Expect(err).Should(HaveOccurred())
157         Expect(err.Error()).To(ContainSubstring("nil message"))
158
159         // nil data for decoding
160         err = msgCodec.DecodeMsg(nil, &memclnt.ControlPingReply{})
161         Expect(err).Should(HaveOccurred())
162         Expect(err.Error()).To(ContainSubstring("panic"))
163 }
164
165 func TestSimpleRequestsWithSequenceNumbers(t *testing.T) {
166         ctx := setupTest(t, false)
167         defer ctx.teardownTest()
168
169         var reqCtx []api.RequestCtx
170         for i := 0; i < 10; i++ {
171                 ctx.mockVpp.MockReply(&memclnt.ControlPingReply{})
172                 req := &memclnt.ControlPing{}
173                 reqCtx = append(reqCtx, ctx.ch.SendRequest(req))
174         }
175
176         for i := 0; i < 10; i++ {
177                 reply := &memclnt.ControlPingReply{}
178                 err := reqCtx[i].ReceiveReply(reply)
179                 Expect(err).ShouldNot(HaveOccurred())
180         }
181 }
182
183 func TestMultiRequestsWithSequenceNumbers(t *testing.T) {
184         ctx := setupTest(t, false)
185         defer ctx.teardownTest()
186
187         var msgs []api.Message
188         for i := 0; i < 10; i++ {
189                 msgs = append(msgs, &interfaces.SwInterfaceDetails{SwIfIndex: interface_types.InterfaceIndex(i)})
190         }
191         ctx.mockVpp.MockReply(msgs...)
192         ctx.mockVpp.MockReply(&memclnt.ControlPingReply{})
193
194         // send multipart request
195         reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
196
197         cnt := 0
198         for {
199                 Expect(cnt < 11).To(BeTrue())
200
201                 // receive a reply
202                 reply := &interfaces.SwInterfaceDetails{}
203                 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
204
205                 if lastReplyReceived {
206                         break
207                 }
208
209                 Expect(err).ShouldNot(HaveOccurred())
210                 Expect(reply.SwIfIndex).To(BeEquivalentTo(cnt))
211
212                 cnt++
213         }
214
215         Expect(cnt).To(BeEquivalentTo(10))
216 }
217
218 func TestSimpleRequestWithTimeout(t *testing.T) {
219         ctx := setupTest(t, true)
220         defer ctx.teardownTest()
221
222         // reply for a previous timeouted requests to be ignored
223         ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
224                 Msg:    &memclnt.ControlPingReply{},
225                 SeqNum: 0,
226         })
227
228         // send reply later
229         req1 := &memclnt.ControlPing{}
230         reqCtx1 := ctx.ch.SendRequest(req1)
231
232         reply := &memclnt.ControlPingReply{}
233         err := reqCtx1.ReceiveReply(reply)
234         Expect(err).ToNot(BeNil())
235         Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
236
237         ctx.mockVpp.MockReplyWithContext(
238                 // reply for the previous request
239                 mock.MsgWithContext{
240                         Msg:    &memclnt.ControlPingReply{},
241                         SeqNum: 1,
242                 },
243                 // reply for the next request
244                 mock.MsgWithContext{
245                         Msg:    &memclnt.ControlPingReply{},
246                         SeqNum: 2,
247                 })
248
249         // next request
250         req2 := &memclnt.ControlPing{}
251         reqCtx2 := ctx.ch.SendRequest(req2)
252
253         // second request should ignore the first reply and return the second one
254         reply = &memclnt.ControlPingReply{}
255         err = reqCtx2.ReceiveReply(reply)
256         Expect(err).To(BeNil())
257 }
258
259 func TestSimpleRequestsWithMissingReply(t *testing.T) {
260         ctx := setupTest(t, false)
261         defer ctx.teardownTest()
262
263         // request without reply
264         req1 := &memclnt.ControlPing{}
265         reqCtx1 := ctx.ch.SendRequest(req1)
266
267         // another request without reply
268         req2 := &memclnt.ControlPing{}
269         reqCtx2 := ctx.ch.SendRequest(req2)
270
271         // third request with reply
272         ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
273                 Msg:    &memclnt.ControlPingReply{},
274                 SeqNum: 3,
275         })
276         req3 := &memclnt.ControlPing{}
277         reqCtx3 := ctx.ch.SendRequest(req3)
278
279         // the first two should fail, but not consume reply for the 3rd
280         reply := &memclnt.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"))
284
285         reply = &memclnt.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"))
289
290         // the second request should succeed
291         reply = &memclnt.ControlPingReply{}
292         err = reqCtx3.ReceiveReply(reply)
293         Expect(err).To(BeNil())
294 }
295
296 func TestMultiRequestsWithErrors(t *testing.T) {
297         ctx := setupTest(t, false)
298         defer ctx.teardownTest()
299
300         // replies for a previous timeouted requests to be ignored
301         msgs := []mock.MsgWithContext{
302                 {Msg: &memclnt.ControlPingReply{}, SeqNum: 0xffff - 1},
303                 {Msg: &memclnt.ControlPingReply{}, SeqNum: 0xffff},
304                 {Msg: &memclnt.ControlPingReply{}, SeqNum: 0},
305         }
306         for i := 0; i < 10; i++ {
307                 msgs = append(msgs, mock.MsgWithContext{
308                         Msg:       &interfaces.SwInterfaceDetails{SwIfIndex: interface_types.InterfaceIndex(i)},
309                         SeqNum:    1,
310                         Multipart: true,
311                 })
312         }
313         // missing finalizing control ping
314
315         // reply for a next request
316         msgs = append(msgs, mock.MsgWithContext{
317                 Msg:    &memclnt.ControlPingReply{},
318                 SeqNum: 2,
319         })
320
321         // queue replies
322         ctx.mockVpp.MockReplyWithContext(msgs...)
323
324         // send multipart request
325         reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
326
327         for i := 0; i < 10; i++ {
328                 // receive multi-part replies
329                 reply := &interfaces.SwInterfaceDetails{}
330                 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
331
332                 Expect(lastReplyReceived).To(BeFalse())
333                 Expect(err).ShouldNot(HaveOccurred())
334                 Expect(reply.SwIfIndex).To(BeEquivalentTo(i))
335         }
336
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"))
342
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"))
347
348         // reply for the second request has not been consumed
349         reqCtx2 := ctx.ch.SendRequest(&memclnt.ControlPing{})
350         reply2 := &memclnt.ControlPingReply{}
351         err = reqCtx2.ReceiveReply(reply2)
352         Expect(err).To(BeNil())
353 }
354
355 func TestRequestsOrdering(t *testing.T) {
356         ctx := setupTest(t, false)
357         defer ctx.teardownTest()
358
359         // the orderings of SendRequest and ReceiveReply calls should match, otherwise
360         // some replies will get thrown away
361
362         // first request
363         ctx.mockVpp.MockReply(&memclnt.ControlPingReply{})
364         req1 := &memclnt.ControlPing{}
365         reqCtx1 := ctx.ch.SendRequest(req1)
366
367         // second request
368         ctx.mockVpp.MockReply(&memclnt.ControlPingReply{})
369         req2 := &memclnt.ControlPing{}
370         reqCtx2 := ctx.ch.SendRequest(req2)
371
372         // if reply for the second request is read first, the reply for the first
373         // request gets thrown away.
374         reply2 := &memclnt.ControlPingReply{}
375         err := reqCtx2.ReceiveReply(reply2)
376         Expect(err).To(BeNil())
377
378         // first request has already been considered closed
379         reply1 := &memclnt.ControlPingReply{}
380         err = reqCtx1.ReceiveReply(reply1)
381         Expect(err).ToNot(BeNil())
382         Expect(err.Error()).To(HavePrefix("no reply received within the timeout period"))
383 }
384
385 func TestCycleOverSetOfSequenceNumbers(t *testing.T) {
386         ctx := setupTest(t, false)
387         defer ctx.teardownTest()
388
389         numIters := 0xffff + 100
390         reqCtx := make(map[int]api.RequestCtx)
391
392         for i := 0; i < numIters+30; i++ {
393                 if i < numIters {
394                         ctx.mockVpp.MockReply(&memclnt.ControlPingReply{})
395                         req := &memclnt.ControlPing{}
396                         reqCtx[i] = ctx.ch.SendRequest(req)
397                 }
398                 if i > 30 {
399                         reply := &memclnt.ControlPingReply{}
400                         err := reqCtx[i-30].ReceiveReply(reply)
401                         Expect(err).ShouldNot(HaveOccurred())
402                 }
403         }
404 }