1 // Copyright (c) 2017 Cisco and/or its affiliates.
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:
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
21 . "github.com/onsi/gomega"
23 "git.fd.io/govpp.git/adapter/mock"
24 "git.fd.io/govpp.git/api"
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/memif"
28 "git.fd.io/govpp.git/binapi/vpe"
32 mockVpp *mock.VppAdapter
37 func setupTest(t *testing.T) *testCtx {
41 mockVpp: mock.NewVppAdapter(),
45 ctx.conn, err = Connect(ctx.mockVpp)
46 Expect(err).ShouldNot(HaveOccurred())
48 ctx.ch, err = ctx.conn.NewAPIChannel()
49 Expect(err).ShouldNot(HaveOccurred())
54 func (ctx *testCtx) teardownTest() {
59 func TestRequestReplyMemifCreate(t *testing.T) {
61 defer ctx.teardownTest()
64 ctx.mockVpp.MockReply(&memif.MemifCreateReply{
68 request := &memif.MemifCreate{
74 reply := &memif.MemifCreateReply{}
76 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
77 Expect(err).ShouldNot(HaveOccurred())
78 Expect(reply.Retval).To(BeEquivalentTo(0),
79 "Incorrect Retval value for MemifCreate")
80 Expect(reply.SwIfIndex).To(BeEquivalentTo(4),
81 "Incorrect SwIfIndex value for MemifCreate")
84 func TestRequestReplyMemifDelete(t *testing.T) {
86 defer ctx.teardownTest()
89 ctx.mockVpp.MockReply(&memif.MemifDeleteReply{})
91 request := &memif.MemifDelete{
94 reply := &memif.MemifDeleteReply{}
96 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
97 Expect(err).ShouldNot(HaveOccurred())
100 func TestRequestReplyMemifDetails(t *testing.T) {
102 defer ctx.teardownTest()
105 ctx.mockVpp.MockReply(&memif.MemifDetails{
107 IfName: "memif-name",
111 request := &memif.MemifDump{}
112 reply := &memif.MemifDetails{}
114 err := ctx.ch.SendRequest(request).ReceiveReply(reply)
115 Expect(err).ShouldNot(HaveOccurred())
116 Expect(reply.SwIfIndex).To(BeEquivalentTo(25),
117 "Incorrect SwIfIndex value for MemifDetails")
118 Expect(reply.IfName).ToNot(BeEmpty(),
119 "MemifDetails IfName is empty byte array")
120 Expect(reply.Role).To(BeEquivalentTo(0),
121 "Incorrect Role value for MemifDetails")
124 func TestMultiRequestReplySwInterfaceMemifDump(t *testing.T) {
126 defer ctx.teardownTest()
129 var msgs []api.Message
130 for i := 1; i <= 10; i++ {
131 msgs = append(msgs, &memif.MemifDetails{
132 SwIfIndex: interface_types.InterfaceIndex(i),
135 ctx.mockVpp.MockReply(msgs...)
136 ctx.mockVpp.MockReply(&ControlPingReply{})
138 reqCtx := ctx.ch.SendMultiRequest(&memif.MemifDump{})
141 msg := &memif.MemifDetails{}
142 stop, err := reqCtx.ReceiveReply(msg)
146 Expect(err).ShouldNot(HaveOccurred())
149 Expect(cnt).To(BeEquivalentTo(10))
152 func TestNotificationEvent(t *testing.T) {
154 defer ctx.teardownTest()
156 // subscribe for notification
157 notifChan := make(chan api.Message, 1)
158 sub, err := ctx.ch.SubscribeNotification(notifChan, &interfaces.SwInterfaceEvent{})
159 Expect(err).ShouldNot(HaveOccurred())
161 // mock event and force its delivery
162 ctx.mockVpp.MockReply(&interfaces.SwInterfaceEvent{
164 Flags: interface_types.IF_STATUS_API_FLAG_LINK_UP,
166 ctx.mockVpp.SendMsg(0, []byte(""))
168 // receive the notification
169 var notif *interfaces.SwInterfaceEvent
170 Eventually(func() *interfaces.SwInterfaceEvent {
172 case n := <-notifChan:
173 notif = n.(*interfaces.SwInterfaceEvent)
178 }).ShouldNot(BeNil())
180 // verify the received notifications
181 Expect(notif.SwIfIndex).To(BeEquivalentTo(2), "Incorrect SwIfIndex value for SwInterfaceSetFlags")
182 Expect(notif.Flags).To(BeEquivalentTo(interface_types.IF_STATUS_API_FLAG_LINK_UP), "Incorrect LinkUpDown value for SwInterfaceSetFlags")
184 err = sub.Unsubscribe()
185 Expect(err).ShouldNot(HaveOccurred())
188 func TestSetReplyTimeout(t *testing.T) {
190 defer ctx.teardownTest()
192 ctx.ch.SetReplyTimeout(time.Millisecond)
195 ctx.mockVpp.MockReply(&ControlPingReply{})
197 // first one request should work
198 err := ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
199 Expect(err).ShouldNot(HaveOccurred())
201 // no other reply ready - expect timeout
202 err = ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
203 Expect(err).Should(HaveOccurred())
204 Expect(err.Error()).To(ContainSubstring("timeout"))
207 func TestSetReplyTimeoutMultiRequest(t *testing.T) {
209 defer ctx.teardownTest()
211 ctx.ch.SetReplyTimeout(time.Millisecond * 100)
214 ctx.mockVpp.MockReply(
215 &interfaces.SwInterfaceDetails{
217 InterfaceName: "if-name-test",
219 &interfaces.SwInterfaceDetails{
221 InterfaceName: "if-name-test",
223 &interfaces.SwInterfaceDetails{
225 InterfaceName: "if-name-test",
228 ctx.mockVpp.MockReply(&ControlPingReply{})
231 sendMultiRequest := func() error {
232 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
234 msg := &interfaces.SwInterfaceDetails{}
235 stop, err := reqCtx.ReceiveReply(msg)
247 // first one request should work
248 err := sendMultiRequest()
249 Expect(err).ShouldNot(HaveOccurred())
251 // no other reply ready - expect timeout
252 err = sendMultiRequest()
253 Expect(err).Should(HaveOccurred())
254 Expect(err.Error()).To(ContainSubstring("timeout"))
256 Expect(cnt).To(BeEquivalentTo(3))
259 func TestReceiveReplyNegative(t *testing.T) {
261 defer ctx.teardownTest()
264 reqCtx1 := &requestCtx{}
265 err := reqCtx1.ReceiveReply(&ControlPingReply{})
266 Expect(err).Should(HaveOccurred())
267 Expect(err.Error()).To(ContainSubstring("invalid request context"))
270 reqCtx2 := &multiRequestCtx{}
271 _, err = reqCtx2.ReceiveReply(&ControlPingReply{})
272 Expect(err).Should(HaveOccurred())
273 Expect(err.Error()).To(ContainSubstring("invalid request context"))
276 reqCtx3 := &requestCtx{}
277 err = reqCtx3.ReceiveReply(nil)
278 Expect(err).Should(HaveOccurred())
279 Expect(err.Error()).To(ContainSubstring("invalid request context"))
282 func TestMultiRequestDouble(t *testing.T) {
284 defer ctx.teardownTest()
287 var msgs []mock.MsgWithContext
288 for i := 1; i <= 3; i++ {
289 msgs = append(msgs, mock.MsgWithContext{
290 Msg: &interfaces.SwInterfaceDetails{
291 SwIfIndex: interface_types.InterfaceIndex(i),
292 InterfaceName: "if-name-test",
298 msgs = append(msgs, mock.MsgWithContext{Msg: &ControlPingReply{}, Multipart: true, SeqNum: 1})
300 for i := 1; i <= 3; i++ {
303 Msg: &interfaces.SwInterfaceDetails{
304 SwIfIndex: interface_types.InterfaceIndex(i),
305 InterfaceName: "if-name-test",
311 msgs = append(msgs, mock.MsgWithContext{Msg: &ControlPingReply{}, Multipart: true, SeqNum: 2})
313 ctx.mockVpp.MockReplyWithContext(msgs...)
316 var sendMultiRequest = func() error {
317 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
319 msg := &interfaces.SwInterfaceDetails{}
320 stop, err := reqCtx.ReceiveReply(msg)
332 err := sendMultiRequest()
333 Expect(err).ShouldNot(HaveOccurred())
335 err = sendMultiRequest()
336 Expect(err).ShouldNot(HaveOccurred())
338 Expect(cnt).To(BeEquivalentTo(6))
341 func TestReceiveReplyAfterTimeout(t *testing.T) {
343 defer ctx.teardownTest()
345 ctx.ch.SetReplyTimeout(time.Millisecond)
348 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &ControlPingReply{}, SeqNum: 1})
349 // first one request should work
351 err := ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
352 Expect(err).ShouldNot(HaveOccurred())
354 err = ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
355 Expect(err).Should(HaveOccurred())
356 Expect(err.Error()).To(ContainSubstring("timeout"))
358 ctx.mockVpp.MockReplyWithContext(
359 // simulating late reply
361 Msg: &ControlPingReply{},
364 // normal reply for next request
366 Msg: &interfaces.SwInterfaceSetFlagsReply{},
371 req := &interfaces.SwInterfaceSetFlags{
373 Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP,
375 reply := &interfaces.SwInterfaceSetFlagsReply{}
378 err = ctx.ch.SendRequest(req).ReceiveReply(reply)
379 Expect(err).ShouldNot(HaveOccurred())
382 func TestReceiveReplyAfterTimeoutMultiRequest(t *testing.T) {
384 TODO: fix mock adapter
385 This test will fail because mock adapter will stop sending replies
386 when it encounters control_ping_reply from multi request,
387 thus never sending reply for next request
392 defer ctx.teardownTest()
394 ctx.ch.SetReplyTimeout(time.Millisecond * 100)
397 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &ControlPingReply{}, SeqNum: 1})
399 // first one request should work
400 err := ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
401 Expect(err).ShouldNot(HaveOccurred())
404 var sendMultiRequest = func() error {
405 reqCtx := ctx.ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
407 msg := &interfaces.SwInterfaceDetails{}
408 stop, err := reqCtx.ReceiveReply(msg)
420 err = sendMultiRequest()
421 Expect(err).Should(HaveOccurred())
422 Expect(err.Error()).To(ContainSubstring("timeout"))
423 Expect(cnt).To(BeEquivalentTo(0))
425 // simulating late replies
426 var msgs []mock.MsgWithContext
427 for i := 1; i <= 3; i++ {
428 msgs = append(msgs, mock.MsgWithContext{
429 Msg: &interfaces.SwInterfaceDetails{
430 SwIfIndex: interface_types.InterfaceIndex(i),
431 InterfaceName: "if-name-test",
437 msgs = append(msgs, mock.MsgWithContext{Msg: &ControlPingReply{}, Multipart: true, SeqNum: 2})
438 ctx.mockVpp.MockReplyWithContext(msgs...)
440 // normal reply for next request
441 ctx.mockVpp.MockReplyWithContext(mock.MsgWithContext{Msg: &interfaces.SwInterfaceSetFlagsReply{}, SeqNum: 3})
443 req := &interfaces.SwInterfaceSetFlags{
445 Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP,
447 reply := &interfaces.SwInterfaceSetFlagsReply{}
450 err = ctx.ch.SendRequest(req).ReceiveReply(reply)
451 Expect(err).ShouldNot(HaveOccurred())
454 func TestInvalidMessageID(t *testing.T) {
456 defer ctx.teardownTest()
459 ctx.mockVpp.MockReply(&vpe.ShowVersionReply{})
460 ctx.mockVpp.MockReply(&vpe.ShowVersionReply{})
462 // first one request should work
463 err := ctx.ch.SendRequest(&vpe.ShowVersion{}).ReceiveReply(&vpe.ShowVersionReply{})
464 Expect(err).ShouldNot(HaveOccurred())
466 // second should fail with error invalid message ID
467 err = ctx.ch.SendRequest(&ControlPing{}).ReceiveReply(&ControlPingReply{})
468 Expect(err).Should(HaveOccurred())
469 Expect(err.Error()).To(ContainSubstring("unexpected message"))