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