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