tests: Add a socket timeout
[vpp.git] / src / vpp-api / vapi / vapi_cpp_test.cpp
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2017 Cisco and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *------------------------------------------------------------------
16  */
17
18 #include <memory>
19 #include <stdio.h>
20 #include <unistd.h>
21 #include <assert.h>
22 #include <setjmp.h>
23 #include <check.h>
24 #include <vapi/memclnt.api.vapi.h>
25 #include <vapi/vapi.hpp>
26 #include <vapi/vpe.api.vapi.hpp>
27 #include <vapi/interface.api.vapi.hpp>
28 #include <vapi/mss_clamp.api.vapi.hpp>
29 #include <fake.api.vapi.hpp>
30
31 DEFINE_VAPI_MSG_IDS_VPE_API_JSON;
32 DEFINE_VAPI_MSG_IDS_INTERFACE_API_JSON;
33 DEFINE_VAPI_MSG_IDS_MSS_CLAMP_API_JSON;
34 DEFINE_VAPI_MSG_IDS_FAKE_API_JSON;
35
36 static char *app_name = nullptr;
37 static char *api_prefix = nullptr;
38 static bool use_uds = false;
39 static const int max_outstanding_requests = 32;
40 static const int response_queue_size = 32;
41
42 #define WAIT_FOR_RESPONSE(param, ret)      \
43   do                                       \
44     {                                      \
45       ret = con.wait_for_response (param); \
46     }                                      \
47   while (ret == VAPI_EAGAIN)
48
49 using namespace vapi;
50
51 void verify_show_version_reply (const Show_version_reply &r)
52 {
53   auto &p = r.get_payload ();
54   printf ("show_version_reply: program: `%s', version: `%s', build directory: "
55           "`%s', build date: `%s'\n",
56           p.program, p.version, p.build_directory, p.build_date);
57   ck_assert_str_eq ("vpe", (char *)p.program);
58 }
59
60 Connection con;
61
62 void setup (void)
63 {
64   vapi_error_e rv =
65     con.connect (app_name, api_prefix, max_outstanding_requests,
66                  response_queue_size, true, use_uds);
67   ck_assert_int_eq (VAPI_OK, rv);
68 }
69
70 void teardown (void)
71 {
72   con.disconnect ();
73 }
74
75 START_TEST (test_show_version_1)
76 {
77   printf ("--- Show version by reading response associated to request ---\n");
78   Show_version sv (con);
79   vapi_error_e rv = sv.execute ();
80   ck_assert_int_eq (VAPI_OK, rv);
81   WAIT_FOR_RESPONSE (sv, rv);
82   ck_assert_int_eq (VAPI_OK, rv);
83   auto &r = sv.get_response ();
84   verify_show_version_reply (r);
85 }
86
87 END_TEST;
88
89 struct Show_version_cb
90 {
91   Show_version_cb () : called{0} {};
92   int called;
93   vapi_error_e operator() (Show_version &sv)
94   {
95     auto &r = sv.get_response ();
96     verify_show_version_reply (r);
97     ++called;
98     return VAPI_OK;
99   }
100 };
101
102 START_TEST (test_show_version_2)
103 {
104   printf ("--- Show version by getting a callback ---\n");
105   Show_version_cb cb;
106   Show_version sv (con, std::ref (cb));
107   vapi_error_e rv = sv.execute ();
108   ck_assert_int_eq (VAPI_OK, rv);
109   con.dispatch (sv);
110   ck_assert_int_eq (1, cb.called);
111 }
112
113 END_TEST;
114
115 START_TEST (test_loopbacks_1)
116 {
117   printf ("--- Create/delete loopbacks by waiting for response ---\n");
118   const auto num_ifs = 5;
119   u8 mac_addresses[num_ifs][6];
120   memset (&mac_addresses, 0, sizeof (mac_addresses));
121   u32 sw_if_indexes[num_ifs];
122   memset (&sw_if_indexes, 0xff, sizeof (sw_if_indexes));
123   for (int i = 0; i < num_ifs; ++i)
124     {
125       memcpy (&mac_addresses[i], "\1\2\3\4\5\6", 6);
126       mac_addresses[i][5] = i;
127     }
128   for (int i = 0; i < num_ifs; ++i)
129     {
130       Create_loopback cl (con);
131       auto &p = cl.get_request ().get_payload ();
132       memcpy (p.mac_address, mac_addresses[i], sizeof (p.mac_address));
133       auto e = cl.execute ();
134       ck_assert_int_eq (VAPI_OK, e);
135       vapi_error_e rv;
136       WAIT_FOR_RESPONSE (cl, rv);
137       ck_assert_int_eq (VAPI_OK, rv);
138       auto &rp = cl.get_response ().get_payload ();
139       ck_assert_int_eq (0, rp.retval);
140       sw_if_indexes[i] = rp.sw_if_index;
141     }
142   for (int i = 0; i < num_ifs; ++i)
143     {
144       printf ("Created loopback with MAC %02x:%02x:%02x:%02x:%02x:%02x --> "
145               "sw_if_index %u\n",
146               mac_addresses[i][0], mac_addresses[i][1], mac_addresses[i][2],
147               mac_addresses[i][3], mac_addresses[i][4], mac_addresses[i][5],
148               sw_if_indexes[i]);
149     }
150
151   { // new context
152     for (int i = 0; i < num_ifs; ++i)
153       {
154         Mss_clamp_enable_disable d (con);
155         auto &req = d.get_request ().get_payload ();
156         req.sw_if_index = sw_if_indexes[i];
157         req.ipv4_mss = 1420;
158         req.ipv4_direction = vapi_enum_mss_clamp_dir::MSS_CLAMP_DIR_RX;
159         auto rv = d.execute ();
160         ck_assert_int_eq (VAPI_OK, rv);
161         WAIT_FOR_RESPONSE (d, rv);
162         ck_assert_int_eq (VAPI_OK, rv);
163       }
164   }
165
166   { // new context
167     bool seen[num_ifs] = { 0 };
168     Mss_clamp_get d (con);
169     d.get_request ().get_payload ().sw_if_index = ~0;
170     auto rv = d.execute ();
171     ck_assert_int_eq (VAPI_OK, rv);
172     WAIT_FOR_RESPONSE (d, rv);
173     ck_assert_int_eq (VAPI_OK, rv);
174     auto &rs = d.get_result_set ();
175     for (auto &r : rs)
176       {
177         auto &p = r.get_payload ();
178         ck_assert_int_eq (p.ipv4_mss, 1420);
179         printf ("tcp-clamp: sw_if_idx %u ip4-mss %d dir %d\n", p.sw_if_index,
180                 p.ipv4_mss, p.ipv4_direction);
181         for (int i = 0; i < num_ifs; ++i)
182           {
183             if (sw_if_indexes[i] == p.sw_if_index)
184               {
185                 ck_assert_int_eq (0, seen[i]);
186                 seen[i] = true;
187               }
188           }
189       }
190     for (int i = 0; i < num_ifs; ++i)
191       {
192         ck_assert_int_eq (1, seen[i]);
193       }
194   }
195
196   { // new context
197     bool seen[num_ifs] = {0};
198     Sw_interface_dump d (con, 0);
199     auto rv = d.execute ();
200     ck_assert_int_eq (VAPI_OK, rv);
201     WAIT_FOR_RESPONSE (d, rv);
202     ck_assert_int_eq (VAPI_OK, rv);
203     auto &rs = d.get_result_set ();
204     for (auto &r : rs)
205       {
206         auto &p = r.get_payload ();
207         for (int i = 0; i < num_ifs; ++i)
208           {
209             if (sw_if_indexes[i] == p.sw_if_index)
210               {
211                 ck_assert_int_eq (0, seen[i]);
212                 seen[i] = true;
213               }
214           }
215       }
216     for (int i = 0; i < num_ifs; ++i)
217       {
218         ck_assert_int_eq (1, seen[i]);
219       }
220   }
221
222   for (int i = 0; i < num_ifs; ++i)
223     {
224       Delete_loopback dl (con);
225       dl.get_request ().get_payload ().sw_if_index = sw_if_indexes[i];
226       auto rv = dl.execute ();
227       ck_assert_int_eq (VAPI_OK, rv);
228       WAIT_FOR_RESPONSE (dl, rv);
229       ck_assert_int_eq (VAPI_OK, rv);
230       auto &response = dl.get_response ();
231       auto rp = response.get_payload ();
232       ck_assert_int_eq (0, rp.retval);
233       printf ("Deleted loopback with sw_if_index %u\n", sw_if_indexes[i]);
234     }
235
236   { // new context
237     Sw_interface_dump d (con, 0);
238     auto rv = d.execute ();
239     ck_assert_int_eq (VAPI_OK, rv);
240     WAIT_FOR_RESPONSE (d, rv);
241     ck_assert_int_eq (VAPI_OK, rv);
242     auto &rs = d.get_result_set ();
243     for (auto &r : rs)
244       {
245         auto &p = r.get_payload ();
246         for (int i = 0; i < num_ifs; ++i)
247           {
248             ck_assert_int_ne (sw_if_indexes[i], p.sw_if_index);
249           }
250       }
251   }
252 }
253
254 END_TEST;
255
256 struct Create_loopback_cb
257 {
258   Create_loopback_cb () : called{ 0 }, sw_if_index{ 0 }, seen{ false } {};
259   int called;
260   u32 sw_if_index;
261   bool seen;
262   vapi_error_e operator() (Create_loopback &cl)
263   {
264     auto &r = cl.get_response ();
265     sw_if_index = r.get_payload ().sw_if_index;
266     ++called;
267     return VAPI_OK;
268   }
269 };
270
271 struct Delete_loopback_cb
272 {
273   Delete_loopback_cb () : called{ 0 }, sw_if_index{ 0 }, seen{ false } {};
274   int called;
275   u32 sw_if_index;
276   bool seen;
277   vapi_error_e operator() (Delete_loopback &dl)
278   {
279     auto &r = dl.get_response ();
280     ck_assert_int_eq (0, r.get_payload ().retval);
281     ++called;
282     return VAPI_OK;
283   }
284 };
285
286 template <int num_ifs> struct Sw_interface_dump_cb
287 {
288   Sw_interface_dump_cb (std::array<Create_loopback_cb, num_ifs> &cbs)
289       : called{0}, cbs{cbs} {};
290   int called;
291   std::array<Create_loopback_cb, num_ifs> &cbs;
292   vapi_error_e operator() (Sw_interface_dump &d)
293   {
294     for (auto &y : cbs)
295       {
296         y.seen = false;
297       }
298     for (auto &x : d.get_result_set ())
299       {
300         auto &p = x.get_payload ();
301         for (auto &y : cbs)
302           {
303             if (p.sw_if_index == y.sw_if_index)
304               {
305                 y.seen = true;
306               }
307           }
308       }
309     for (auto &y : cbs)
310       {
311         ck_assert_int_eq (true, y.seen);
312       }
313     ++called;
314     return VAPI_OK;
315   }
316 };
317
318 START_TEST (test_loopbacks_2)
319 {
320   printf ("--- Create/delete loopbacks by getting a callback ---\n");
321   const auto num_ifs = 5;
322   u8 mac_addresses[num_ifs][6];
323   memset (&mac_addresses, 0, sizeof (mac_addresses));
324   for (int i = 0; i < num_ifs; ++i)
325     {
326       memcpy (&mac_addresses[i], "\1\2\3\4\5\6", 6);
327       mac_addresses[i][5] = i;
328     }
329   std::array<Create_loopback_cb, num_ifs> ccbs;
330   std::array<std::unique_ptr<Create_loopback>, num_ifs> clcs;
331   for (int i = 0; i < num_ifs; ++i)
332     {
333       Create_loopback *cl = new Create_loopback (con, std::ref (ccbs[i]));
334       clcs[i].reset (cl);
335       auto &p = cl->get_request ().get_payload ();
336       memcpy (p.mac_address, mac_addresses[i], sizeof (p.mac_address));
337       auto e = cl->execute ();
338       ck_assert_int_eq (VAPI_OK, e);
339     }
340   con.dispatch ();
341   for (int i = 0; i < num_ifs; ++i)
342     {
343       ck_assert_int_eq (1, ccbs[i].called);
344       printf ("Created loopback with MAC %02x:%02x:%02x:%02x:%02x:%02x --> "
345               "sw_if_index %u\n",
346               mac_addresses[i][0], mac_addresses[i][1], mac_addresses[i][2],
347               mac_addresses[i][3], mac_addresses[i][4], mac_addresses[i][5],
348               ccbs[i].sw_if_index);
349     }
350
351   Sw_interface_dump_cb<num_ifs> swdcb (ccbs);
352   Sw_interface_dump d (con, 0, std::ref (swdcb));
353   auto rv = d.execute ();
354   ck_assert_int_eq (VAPI_OK, rv);
355   WAIT_FOR_RESPONSE (d, rv);
356   ck_assert_int_eq (VAPI_OK, rv);
357   ck_assert_int_ne (0, swdcb.called);
358   std::array<Delete_loopback_cb, num_ifs> dcbs;
359   std::array<std::unique_ptr<Delete_loopback>, num_ifs> dlcs;
360   for (int i = 0; i < num_ifs; ++i)
361     {
362       Delete_loopback *dl = new Delete_loopback (con, std::ref (dcbs[i]));
363       dlcs[i].reset (dl);
364       auto &p = dl->get_request ().get_payload ();
365       p.sw_if_index = ccbs[i].sw_if_index;
366       dcbs[i].sw_if_index = ccbs[i].sw_if_index;
367       auto e = dl->execute ();
368       ck_assert_int_eq (VAPI_OK, e);
369     }
370   con.dispatch ();
371   for (auto &x : dcbs)
372     {
373       ck_assert_int_eq (true, x.called);
374       printf ("Deleted loopback with sw_if_index %u\n", x.sw_if_index);
375     }
376
377   { // new context
378     Sw_interface_dump d (con, 0);
379     auto rv = d.execute ();
380     ck_assert_int_eq (VAPI_OK, rv);
381     WAIT_FOR_RESPONSE (d, rv);
382     ck_assert_int_eq (VAPI_OK, rv);
383     auto &rs = d.get_result_set ();
384     for (auto &r : rs)
385       {
386         auto &p = r.get_payload ();
387         for (int i = 0; i < num_ifs; ++i)
388           {
389             ck_assert_int_ne (ccbs[i].sw_if_index, p.sw_if_index);
390           }
391       }
392   }
393 }
394
395 END_TEST;
396
397 START_TEST (test_unsupported)
398 {
399   printf ("--- Unsupported messages ---\n");
400   bool thrown = false;
401   try
402     {
403       Test_fake_msg fake (con);
404     }
405   catch (const Msg_not_available_exception &)
406     {
407       thrown = true;
408       printf ("Constructing unsupported msg not possible - test pass.\n");
409     }
410   ck_assert_int_eq (true, thrown);
411   thrown = false;
412   try
413     {
414       Test_fake_dump fake (con);
415     }
416   catch (const Msg_not_available_exception &)
417     {
418       thrown = true;
419       printf ("Constructing unsupported dump not possible - test pass.\n");
420     }
421   ck_assert_int_eq (true, thrown);
422   thrown = false;
423   try
424     {
425       Event_registration<Test_fake_details> fake (con);
426     }
427   catch (const Msg_not_available_exception &)
428     {
429       thrown = true;
430       printf ("Constructing unsupported event registration not possible - "
431               "test pass.\n");
432     }
433   ck_assert_int_eq (true, thrown);
434 }
435
436 END_TEST;
437
438 Suite *test_suite (void)
439 {
440   Suite *s = suite_create ("VAPI test");
441
442   TCase *tc_cpp_api = tcase_create ("C++ API");
443   tcase_set_timeout (tc_cpp_api, 25);
444   tcase_add_checked_fixture (tc_cpp_api, setup, teardown);
445   tcase_add_test (tc_cpp_api, test_show_version_1);
446   tcase_add_test (tc_cpp_api, test_show_version_2);
447   tcase_add_test (tc_cpp_api, test_loopbacks_1);
448   tcase_add_test (tc_cpp_api, test_loopbacks_2);
449   tcase_add_test (tc_cpp_api, test_unsupported);
450   suite_add_tcase (s, tc_cpp_api);
451
452   return s;
453 }
454
455 int main (int argc, char *argv[])
456 {
457   if (4 != argc)
458     {
459       printf ("Invalid argc==`%d'\n", argc);
460       return EXIT_FAILURE;
461     }
462   app_name = argv[1];
463   api_prefix = argv[2];
464   if (!strcmp (argv[3], "shm"))
465     use_uds = 0;
466   else if (!strcmp (argv[3], "uds"))
467     use_uds = 1;
468   else
469     {
470       printf ("Unrecognised required argument '%s', expected 'uds' or 'shm'.",
471               argv[3]);
472       return EXIT_FAILURE;
473     }
474   printf ("App name: `%s', API prefix: `%s', use unix sockets %d\n", app_name,
475           api_prefix, use_uds);
476
477   int number_failed;
478   Suite *s;
479   SRunner *sr;
480
481   s = test_suite ();
482   sr = srunner_create (s);
483
484   srunner_run_all (sr, CK_NORMAL);
485   number_failed = srunner_ntests_failed (sr);
486   srunner_free (sr);
487   return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
488 }
489
490 /*
491  * fd.io coding-style-patch-verification: ON
492  *
493  * Local Variables:
494  * eval: (c-set-style "gnu")
495  * End:
496  */