socketclient: wait for socket to be created
[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/vpe"
26         . "github.com/onsi/gomega"
27 )
28
29 type testCtx struct {
30         mockVpp *mock.VppAdapter
31         conn    *core.Connection
32         ch      api.Channel
33 }
34
35 func setupTest(t *testing.T, bufferedChan bool) *testCtx {
36         RegisterTestingT(t)
37
38         ctx := &testCtx{
39                 mockVpp: mock.NewVppAdapter(),
40         }
41
42         var err error
43         ctx.conn, err = core.Connect(ctx.mockVpp)
44         Expect(err).ShouldNot(HaveOccurred())
45
46         if bufferedChan {
47                 ctx.ch, err = ctx.conn.NewAPIChannelBuffered(100, 100)
48         } else {
49                 ctx.ch, err = ctx.conn.NewAPIChannel()
50         }
51         Expect(err).ShouldNot(HaveOccurred())
52
53         return ctx
54 }
55
56 func (ctx *testCtx) teardownTest() {
57         ctx.ch.Close()
58         ctx.conn.Disconnect()
59 }
60
61 func TestNilConnection(t *testing.T) {
62         RegisterTestingT(t)
63         var conn *core.Connection
64
65         ch, err := conn.NewAPIChannel()
66         Expect(ch).Should(BeNil())
67         Expect(err).Should(HaveOccurred())
68         Expect(err.Error()).To(ContainSubstring("nil"))
69
70         ch, err = conn.NewAPIChannelBuffered(1, 1)
71         Expect(ch).Should(BeNil())
72         Expect(err).Should(HaveOccurred())
73         Expect(err.Error()).To(ContainSubstring("nil"))
74 }
75
76 func TestAsyncConnection(t *testing.T) {
77         ctx := setupTest(t, false)
78         defer ctx.teardownTest()
79
80         ctx.conn.Disconnect()
81         conn, statusChan, err := core.AsyncConnect(ctx.mockVpp, core.DefaultMaxReconnectAttempts, core.DefaultReconnectInterval)
82         ctx.conn = conn
83
84         Expect(err).ShouldNot(HaveOccurred())
85         Expect(conn).ShouldNot(BeNil())
86
87         ev := <-statusChan
88         Expect(ev.State).Should(BeEquivalentTo(core.Connected))
89 }
90
91 func TestCodec(t *testing.T) {
92         RegisterTestingT(t)
93
94         msgCodec := &codec.MsgCodec{}
95
96         // request
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())
100
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}))
105
106         // reply
107         data, err = msgCodec.EncodeMsg(&vpe.ControlPingReply{Retval: 55}, 22)
108         Expect(err).ShouldNot(HaveOccurred())
109         Expect(data).ShouldNot(BeEmpty())
110
111         msg2 := &vpe.ControlPingReply{}
112         err = msgCodec.DecodeMsg(data, msg2)
113         Expect(err).ShouldNot(HaveOccurred())
114         Expect(msg2.Retval).To(BeEquivalentTo(55))
115 }
116
117 func TestCodecNegative(t *testing.T) {
118         RegisterTestingT(t)
119
120         msgCodec := &codec.MsgCodec{}
121
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())
127
128         // nil message for decoding
129         err = msgCodec.DecodeMsg(data, nil)
130         Expect(err).Should(HaveOccurred())
131         Expect(err.Error()).To(ContainSubstring("nil message"))
132
133         // nil data for decoding
134         err = msgCodec.DecodeMsg(nil, &vpe.ControlPingReply{})
135         Expect(err).Should(HaveOccurred())
136         Expect(err.Error()).To(ContainSubstring("EOF"))
137 }
138
139 func TestSimpleRequestsWithSequenceNumbers(t *testing.T) {
140         ctx := setupTest(t, false)
141         defer ctx.teardownTest()
142
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))
148         }
149
150         for i := 0; i < 10; i++ {
151                 reply := &vpe.ControlPingReply{}
152                 err := reqCtx[i].ReceiveReply(reply)
153                 Expect(err).ShouldNot(HaveOccurred())
154         }
155 }
156
157 func TestMultiRequestsWithSequenceNumbers(t *testing.T) {
158         ctx := setupTest(t, false)
159         defer ctx.teardownTest()
160
161         var msgs []api.Message
162         for i := 0; i < 10; i++ {
163                 msgs = append(msgs, &interfaces.SwInterfaceDetails{SwIfIndex: uint32(i)})
164         }
165         ctx.mockVpp.MockReply(msgs...)
166         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
167
168         // send multipart request
169         reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
170
171         cnt := 0
172         for {
173                 Expect(cnt < 11).To(BeTrue())
174
175                 // receive a reply
176                 reply := &interfaces.SwInterfaceDetails{}
177                 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
178
179                 if lastReplyReceived {
180                         break
181                 }
182
183                 Expect(err).ShouldNot(HaveOccurred())
184                 Expect(reply.SwIfIndex).To(BeEquivalentTo(cnt))
185
186                 cnt++
187         }
188
189         Expect(cnt).To(BeEquivalentTo(10))
190 }
191
192 func TestSimpleRequestWithTimeout(t *testing.T) {
193         ctx := setupTest(t, true)
194         defer ctx.teardownTest()
195
196         // reply for a previous timeouted requests to be ignored
197         ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
198                 Msg:    &vpe.ControlPingReply{},
199                 SeqNum: 0,
200         })
201
202         // send reply later
203         req1 := &vpe.ControlPing{}
204         reqCtx1 := ctx.ch.SendRequest(req1)
205
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"))
210
211         ctx.mockVpp.MockReplyWithContext(
212                 // reply for the previous request
213                 mock.MsgWithContext{
214                         Msg:    &vpe.ControlPingReply{},
215                         SeqNum: 1,
216                 },
217                 // reply for the next request
218                 mock.MsgWithContext{
219                         Msg:    &vpe.ControlPingReply{},
220                         SeqNum: 2,
221                 })
222
223         // next request
224         req2 := &vpe.ControlPing{}
225         reqCtx2 := ctx.ch.SendRequest(req2)
226
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())
231 }
232
233 func TestSimpleRequestsWithMissingReply(t *testing.T) {
234         ctx := setupTest(t, false)
235         defer ctx.teardownTest()
236
237         // request without reply
238         req1 := &vpe.ControlPing{}
239         reqCtx1 := ctx.ch.SendRequest(req1)
240
241         // another request without reply
242         req2 := &vpe.ControlPing{}
243         reqCtx2 := ctx.ch.SendRequest(req2)
244
245         // third request with reply
246         ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{
247                 Msg:    &vpe.ControlPingReply{},
248                 SeqNum: 3,
249         })
250         req3 := &vpe.ControlPing{}
251         reqCtx3 := ctx.ch.SendRequest(req3)
252
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"))
258
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"))
263
264         // the second request should succeed
265         reply = &vpe.ControlPingReply{}
266         err = reqCtx3.ReceiveReply(reply)
267         Expect(err).To(BeNil())
268 }
269
270 func TestMultiRequestsWithErrors(t *testing.T) {
271         ctx := setupTest(t, false)
272         defer ctx.teardownTest()
273
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},
279         }
280         for i := 0; i < 10; i++ {
281                 msgs = append(msgs, mock.MsgWithContext{
282                         Msg:       &interfaces.SwInterfaceDetails{SwIfIndex: uint32(i)},
283                         SeqNum:    1,
284                         Multipart: true,
285                 })
286         }
287         // missing finalizing control ping
288
289         // reply for a next request
290         msgs = append(msgs, mock.MsgWithContext{
291                 Msg:    &vpe.ControlPingReply{},
292                 SeqNum: 2,
293         })
294
295         // queue replies
296         ctx.mockVpp.MockReplyWithContext(msgs...)
297
298         // send multipart request
299         reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
300
301         for i := 0; i < 10; i++ {
302                 // receive multi-part replies
303                 reply := &interfaces.SwInterfaceDetails{}
304                 lastReplyReceived, err := reqCtx.ReceiveReply(reply)
305
306                 Expect(lastReplyReceived).To(BeFalse())
307                 Expect(err).ShouldNot(HaveOccurred())
308                 Expect(reply.SwIfIndex).To(BeEquivalentTo(i))
309         }
310
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"))
316
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"))
321
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())
327 }
328
329 func TestRequestsOrdering(t *testing.T) {
330         ctx := setupTest(t, false)
331         defer ctx.teardownTest()
332
333         // the orderings of SendRequest and ReceiveReply calls should match, otherwise
334         // some replies will get thrown away
335
336         // first request
337         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
338         req1 := &vpe.ControlPing{}
339         reqCtx1 := ctx.ch.SendRequest(req1)
340
341         // second request
342         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
343         req2 := &vpe.ControlPing{}
344         reqCtx2 := ctx.ch.SendRequest(req2)
345
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())
351
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"))
357 }
358
359 func TestCycleOverSetOfSequenceNumbers(t *testing.T) {
360         ctx := setupTest(t, false)
361         defer ctx.teardownTest()
362
363         numIters := 0xffff + 100
364         reqCtx := make(map[int]api.RequestCtx)
365
366         for i := 0; i < numIters+30; i++ {
367                 if i < numIters {
368                         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
369                         req := &vpe.ControlPing{}
370                         reqCtx[i] = ctx.ch.SendRequest(req)
371                 }
372                 if i > 30 {
373                         reply := &vpe.ControlPingReply{}
374                         err := reqCtx[i-30].ReceiveReply(reply)
375                         Expect(err).ShouldNot(HaveOccurred())
376                 }
377         }
378 }