Merge "Mock Adapter: Switch back to handlers once the queue is empty."
[govpp.git] / core / core_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/core"
23         "git.fd.io/govpp.git/core/bin_api/vpe"
24         "git.fd.io/govpp.git/examples/bin_api/interfaces"
25
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) *testCtx {
36         RegisterTestingT(t)
37
38         ctx := &testCtx{}
39         ctx.mockVpp = &mock.VppAdapter{}
40
41         var err error
42         ctx.conn, err = core.Connect(ctx.mockVpp)
43         Expect(err).ShouldNot(HaveOccurred())
44
45         ctx.ch, err = ctx.conn.NewAPIChannel()
46         Expect(err).ShouldNot(HaveOccurred())
47
48         return ctx
49 }
50
51 func (ctx *testCtx) teardownTest() {
52         ctx.ch.Close()
53         ctx.conn.Disconnect()
54 }
55
56 func TestSimpleRequest(t *testing.T) {
57         ctx := setupTest(t)
58         defer ctx.teardownTest()
59
60         ctx.mockVpp.MockReply(&vpe.ControlPingReply{Retval: -5})
61
62         req := &vpe.ControlPing{}
63         reply := &vpe.ControlPingReply{}
64
65         // send the request and receive a reply
66         ctx.ch.ReqChan <- &api.VppRequest{Message: req}
67         vppReply := <-ctx.ch.ReplyChan
68
69         Expect(vppReply).ShouldNot(BeNil())
70         Expect(vppReply.Error).ShouldNot(HaveOccurred())
71
72         // decode the message
73         err := ctx.ch.MsgDecoder.DecodeMsg(vppReply.Data, reply)
74         Expect(err).ShouldNot(HaveOccurred())
75
76         Expect(reply.Retval).To(BeEquivalentTo(-5))
77 }
78
79 func TestMultiRequest(t *testing.T) {
80         ctx := setupTest(t)
81         defer ctx.teardownTest()
82
83         for m := 0; m < 10; m++ {
84                 ctx.mockVpp.MockReply(&interfaces.SwInterfaceDetails{})
85         }
86         ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
87
88         // send multipart request
89         ctx.ch.ReqChan <- &api.VppRequest{Message: &interfaces.SwInterfaceDump{}, Multipart: true}
90
91         cnt := 0
92         for {
93                 // receive a reply
94                 vppReply := <-ctx.ch.ReplyChan
95                 if vppReply.LastReplyReceived {
96                         break // break out of the loop
97                 }
98                 Expect(vppReply.Error).ShouldNot(HaveOccurred())
99
100                 // decode the message
101                 reply := &interfaces.SwInterfaceDetails{}
102                 err := ctx.ch.MsgDecoder.DecodeMsg(vppReply.Data, reply)
103                 Expect(err).ShouldNot(HaveOccurred())
104                 cnt++
105         }
106
107         Expect(cnt).To(BeEquivalentTo(10))
108 }
109
110 func TestNotifications(t *testing.T) {
111         ctx := setupTest(t)
112         defer ctx.teardownTest()
113
114         // subscribe for notification
115         notifChan := make(chan api.Message, 1)
116         subscription := &api.NotifSubscription{
117                 NotifChan:  notifChan,
118                 MsgFactory: interfaces.NewSwInterfaceSetFlags,
119         }
120         ctx.ch.NotifSubsChan <- &api.NotifSubscribeRequest{
121                 Subscription: subscription,
122                 Subscribe:    true,
123         }
124         err := <-ctx.ch.NotifSubsReplyChan
125         Expect(err).ShouldNot(HaveOccurred())
126
127         // mock the notification and force its delivery
128         ctx.mockVpp.MockReply(&interfaces.SwInterfaceSetFlags{
129                 SwIfIndex:   3,
130                 AdminUpDown: 1,
131         })
132         ctx.mockVpp.SendMsg(0, []byte{0})
133
134         // receive the notification
135         notif := (<-notifChan).(*interfaces.SwInterfaceSetFlags)
136
137         Expect(notif.SwIfIndex).To(BeEquivalentTo(3))
138
139         // unsubscribe notification
140         ctx.ch.NotifSubsChan <- &api.NotifSubscribeRequest{
141                 Subscription: subscription,
142                 Subscribe:    false,
143         }
144         err = <-ctx.ch.NotifSubsReplyChan
145         Expect(err).ShouldNot(HaveOccurred())
146 }
147
148 func TestNilConnection(t *testing.T) {
149         RegisterTestingT(t)
150         var conn *core.Connection
151
152         ch, err := conn.NewAPIChannel()
153         Expect(ch).Should(BeNil())
154         Expect(err).Should(HaveOccurred())
155         Expect(err.Error()).To(ContainSubstring("nil"))
156
157         ch, err = conn.NewAPIChannelBuffered(1, 1)
158         Expect(ch).Should(BeNil())
159         Expect(err).Should(HaveOccurred())
160         Expect(err.Error()).To(ContainSubstring("nil"))
161 }
162
163 func TestDoubleConnection(t *testing.T) {
164         ctx := setupTest(t)
165         defer ctx.teardownTest()
166
167         conn, err := core.Connect(ctx.mockVpp)
168         Expect(err).Should(HaveOccurred())
169         Expect(err.Error()).To(ContainSubstring("only one connection per process"))
170         Expect(conn).Should(BeNil())
171 }
172
173 func TestAsyncConnection(t *testing.T) {
174         ctx := setupTest(t)
175         defer ctx.teardownTest()
176
177         ctx.conn.Disconnect()
178         conn, ch, err := core.AsyncConnect(ctx.mockVpp)
179         ctx.conn = conn
180
181         Expect(err).ShouldNot(HaveOccurred())
182         Expect(conn).ShouldNot(BeNil())
183
184         ev := <-ch
185         Expect(ev.State).Should(BeEquivalentTo(core.Connected))
186 }
187
188 func TestFullBuffer(t *testing.T) {
189         ctx := setupTest(t)
190         defer ctx.teardownTest()
191
192         // close the default API channel
193         ctx.ch.Close()
194
195         // create a new channel with limited buffer sizes
196         var err error
197         ctx.ch, err = ctx.conn.NewAPIChannelBuffered(10, 1)
198         Expect(err).ShouldNot(HaveOccurred())
199
200         // send multiple requests, only one reply should be read
201         for i := 0; i < 20; i++ {
202                 ctx.mockVpp.MockReply(&vpe.ControlPingReply{})
203                 ctx.ch.ReqChan <- &api.VppRequest{Message: &vpe.ControlPing{}}
204         }
205
206         vppReply := <-ctx.ch.ReplyChan
207         Expect(vppReply).ShouldNot(BeNil())
208
209         received := false
210         select {
211         case vppReply = <-ctx.ch.ReplyChan:
212                 received = true // this should not happen
213         default:
214                 received = false // no reply to be received
215         }
216         Expect(received).Should(BeFalse(), "A reply has been recieved, should had been ignored.")
217 }
218
219 func TestCodec(t *testing.T) {
220         RegisterTestingT(t)
221
222         codec := &core.MsgCodec{}
223
224         // request
225         data, err := codec.EncodeMsg(&vpe.CreateLoopback{MacAddress: []byte{1, 2, 3, 4, 5, 6}}, 11)
226         Expect(err).ShouldNot(HaveOccurred())
227         Expect(data).ShouldNot(BeEmpty())
228
229         msg1 := &vpe.CreateLoopback{}
230         err = codec.DecodeMsg(data, msg1)
231         Expect(err).ShouldNot(HaveOccurred())
232         Expect(msg1.MacAddress).To(BeEquivalentTo([]byte{1, 2, 3, 4, 5, 6}))
233
234         // reply
235         data, err = codec.EncodeMsg(&vpe.ControlPingReply{Retval: 55}, 22)
236         Expect(err).ShouldNot(HaveOccurred())
237         Expect(data).ShouldNot(BeEmpty())
238
239         msg2 := &vpe.ControlPingReply{}
240         err = codec.DecodeMsg(data, msg2)
241         Expect(err).ShouldNot(HaveOccurred())
242         Expect(msg2.Retval).To(BeEquivalentTo(55))
243
244         // other
245         data, err = codec.EncodeMsg(&vpe.VnetIP4FibCounters{VrfID: 77}, 33)
246         Expect(err).ShouldNot(HaveOccurred())
247         Expect(data).ShouldNot(BeEmpty())
248
249         msg3 := &vpe.VnetIP4FibCounters{}
250         err = codec.DecodeMsg(data, msg3)
251         Expect(err).ShouldNot(HaveOccurred())
252         Expect(msg3.VrfID).To(BeEquivalentTo(77))
253 }
254
255 func TestCodecNegative(t *testing.T) {
256         RegisterTestingT(t)
257
258         codec := &core.MsgCodec{}
259
260         // nil message for encoding
261         data, err := codec.EncodeMsg(nil, 15)
262         Expect(err).Should(HaveOccurred())
263         Expect(err.Error()).To(ContainSubstring("nil message"))
264         Expect(data).Should(BeNil())
265
266         // nil message for decoding
267         err = codec.DecodeMsg(data, nil)
268         Expect(err).Should(HaveOccurred())
269         Expect(err.Error()).To(ContainSubstring("nil message"))
270
271         // nil data for decoding
272         err = codec.DecodeMsg(nil, &vpe.ControlPingReply{})
273         Expect(err).Should(HaveOccurred())
274         Expect(err.Error()).To(ContainSubstring("EOF"))
275 }