fix div by zero
[oweals/gnunet.git] / src / transport / test_transport_address_switch.c
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2009, 2010, 2011, 2016 GNUnet e.V.
4
5  GNUnet is free software: you can redistribute it and/or modify it
6  under the terms of the GNU Affero General Public License as published
7  by the Free Software Foundation, either version 3 of the License,
8  or (at your option) any later version.
9
10  GNUnet is distributed in the hope that it will be useful, but
11  WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  Affero General Public License for more details.
14
15  You should have received a copy of the GNU Affero General Public License
16  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 /**
19  * @file transport/test_transport_address_switch.c
20  * @brief base test case for transport implementations
21  *
22  * This test case tests if peers can successfully switch addresses when
23  * connected for plugins supporting multiple addresses by monitoring transport's
24  * statistic values.
25  *
26  * This test starts 2 peers and connects them. When connected test messages
27  * are transmitted from peer 2 to peer 1. The test monitors transport's
28  * statistics values for information about address switch attempts.
29  *
30  * The test passes with success if one of the peers could successfully switch
31  * addresses in connected state and a test message was successfully transmitted
32  * after this switch.
33  *
34  * Since it is not possible to trigger an address switch from outside,
35  * the test returns "77" (skipped) when no address switching attempt
36  * takes place. It fails if an address switch attempt fails.
37  *
38  * NOTE: The test seems largely useless right now, as we simply NEVER
39  * switch addresses under the test conditions.  However, it may be a
40  * good starting point for a future test.  For now, it always times
41  * out and returns "77" (skipped), so we set the timeout suitably low.
42  */
43 #include "platform.h"
44 #include "gnunet_transport_service.h"
45 #include "gnunet_ats_service.h"
46 #include "transport-testing.h"
47
48
49 /**
50  * Testcase timeout (set aggressively as we know this test doesn't work right now)
51  */
52 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
53
54
55 static struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc;
56
57 static struct GNUNET_SCHEDULER_Task *measure_task;
58
59
60 /**
61  * Statistics we track per peer.
62  */
63 struct PeerStats
64 {
65   struct GNUNET_STATISTICS_Handle *stat;
66
67   unsigned int addresses_avail;
68
69   unsigned int switch_attempts;
70
71   unsigned int switch_success;
72
73   unsigned int switch_fail;
74 };
75
76 static struct PeerStats stats[2];
77
78 /* Amount of data transfered since last switch attempt */
79 static unsigned long long bytes_sent_after_switch;
80
81 static unsigned long long bytes_recv_after_switch;
82
83
84 static int
85 stat_start_attempt_cb (void *cls,
86                        const char *subsystem,
87                        const char *name,
88                        uint64_t value,
89                        int is_persistent)
90 {
91   struct PeerStats *stat = cls;
92
93   stat->switch_attempts++;
94   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
95               "Switch attempted (%p)",
96               stat);
97   bytes_recv_after_switch = 0;
98   bytes_sent_after_switch = 0;
99
100   return GNUNET_OK;
101 }
102
103
104 static int
105 stat_success_attempt_cb (void *cls,
106                          const char *subsystem,
107                          const char *name,
108                          uint64_t value,
109                          int is_persistent)
110 {
111   struct PeerStats *stat = cls;
112
113   stat->switch_success++;
114   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
115               "Switch succeeded (%p)",
116               stat);
117   return GNUNET_OK;
118 }
119
120
121 static int
122 stat_fail_attempt_cb (void *cls,
123                       const char *subsystem,
124                       const char *name,
125                       uint64_t value,
126                       int is_persistent)
127 {
128   struct PeerStats *stat = cls;
129
130   if (value == 0)
131     return GNUNET_OK;
132
133   stat->switch_fail++;
134   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
135               "Switch failed (%p)",
136               stat);
137   return GNUNET_OK;
138 }
139
140
141 static int
142 stat_addresses_available (void *cls,
143                           const char *subsystem,
144                           const char *name,
145                           uint64_t value,
146                           int is_persistent)
147 {
148   struct PeerStats *stat = cls;
149
150   stat->addresses_avail++;
151   return GNUNET_OK;
152 }
153
154
155 /**
156  * List of statistics entries we care about.
157  */
158 static struct WatchEntry {
159
160   /**
161    * Name of the statistic we watch.
162    */
163   const char *stat_name;
164
165   /**
166    * Handler to register;
167    */
168   GNUNET_STATISTICS_Iterator stat_handler;
169 } watches[] = {
170   { "# Attempts to switch addresses", &stat_start_attempt_cb },
171   { "# Successful attempts to switch addresses", &stat_success_attempt_cb },
172   { "# Failed attempts to switch addresses (failed to send CONNECT CONT)", &stat_fail_attempt_cb },
173   { "# Failed attempts to switch addresses (failed to send CONNECT)", &stat_fail_attempt_cb },
174   { "# Failed attempts to switch addresses (no response)", &stat_fail_attempt_cb },
175   { "# transport addresses", &stat_addresses_available },
176   { NULL, NULL }
177 };
178
179
180 static void
181 custom_shutdown (void *cls)
182 {
183   int result;
184
185   if (NULL != measure_task)
186   {
187     GNUNET_SCHEDULER_cancel (measure_task);
188     measure_task = NULL;
189   }
190   if (0 == stats[0].switch_attempts + stats[1].switch_attempts)
191   {
192     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
193                 "Test did not work, as peers didn't switch (flawed testcase)!\n");
194     ccc->global_ret = 77;
195   }
196   else
197   {
198     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
199                 "Fail (timeout)! No transmission after switch! Stopping peers\n");
200     ccc->global_ret = 77; /* GNUNET_SYSERR; */
201   }
202
203   /* stop statistics */
204   for (unsigned int i=0;i<2;i++)
205   {
206     if (NULL != stats[i].stat)
207     {
208       for (unsigned int j=0;NULL != watches[j].stat_name; j++)
209         GNUNET_STATISTICS_watch_cancel (stats[i].stat,
210                                         "transport",
211                                         watches[j].stat_name,
212                                         watches[j].stat_handler,
213                                         &stats[i]);
214
215       GNUNET_STATISTICS_destroy (stats[i].stat,
216                                  GNUNET_NO);
217       stats[i].stat = NULL;
218     }
219   }
220
221   result = 0;
222   FPRINTF (stderr, "\n");
223   if (stats[0].switch_attempts > 0)
224   {
225     FPRINTF (stderr,
226              "Peer 1 tried %u times to switch and succeeded %u times, failed %u times\n",
227              stats[0].switch_attempts,
228              stats[0].switch_success,
229              stats[0].switch_fail);
230     if (stats[0].switch_success != stats[0].switch_attempts)
231     {
232       GNUNET_break (0);
233       result ++;
234     }
235   }
236   else if (stats[0].addresses_avail > 1)
237   {
238     FPRINTF (stderr,
239              "Peer 1 had %u addresses available, but did not try to switch\n",
240              stats[0].addresses_avail);
241   }
242   if (stats[1].switch_attempts > 0)
243   {
244     FPRINTF (stderr,
245              "Peer 2 tried %u times to switch and succeeded %u times, failed %u times\n",
246              stats[1].switch_attempts,
247              stats[1].switch_success,
248              stats[1].switch_fail);
249     if (stats[1].switch_success != stats[1].switch_attempts)
250     {
251       GNUNET_break (0);
252       result++;
253     }
254   }
255   else if (stats[1].addresses_avail > 1)
256   {
257     FPRINTF (stderr,
258              "Peer 2 had %u addresses available, but did not try to switch\n",
259              stats[1].addresses_avail);
260   }
261
262   if ( ((stats[0].switch_attempts > 0) || (stats[1].switch_attempts > 0)) &&
263        (bytes_sent_after_switch == 0) )
264   {
265     FPRINTF (stderr,
266              "No data sent after switching!\n");
267     GNUNET_break (0);
268     result++;
269   }
270   if ( ((stats[0].switch_attempts > 0) || (stats[1].switch_attempts > 0)) &&
271        (bytes_recv_after_switch == 0) )
272   {
273     FPRINTF (stderr,
274              "No data received after switching!\n");
275     GNUNET_break (0);
276     result++;
277   }
278 #if 0
279   /* This test is not really expected to pass right now... */
280   if (0 != result)
281     ccc->global_ret = GNUNET_SYSERR;
282 #endif
283 }
284
285
286 static void
287 notify_receive (void *cls,
288                 struct GNUNET_TRANSPORT_TESTING_PeerContext *receiver,
289                 const struct GNUNET_PeerIdentity *sender,
290                 const struct GNUNET_TRANSPORT_TESTING_TestMessage *hdr)
291 {
292   if (GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE != ntohs (hdr->header.type))
293     return;
294
295   {
296     char *ps = GNUNET_strdup (GNUNET_i2s (&receiver->id));
297
298     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
299                 "Peer %u (`%s') got message %u of size %u from peer (`%s')\n",
300                 receiver->no,
301                 ps,
302                 (uint32_t) ntohl (hdr->num),
303                 ntohs (hdr->header.size),
304                 GNUNET_i2s (sender));
305     GNUNET_free (ps);
306   }
307   if ( ((stats[0].switch_attempts >= 1) || (stats[1].switch_attempts >= 1)) &&
308         (stats[0].switch_attempts == stats[0].switch_fail + stats[0].switch_success) &&
309         (stats[1].switch_attempts == stats[1].switch_fail + stats[1].switch_success) )
310   {
311     bytes_recv_after_switch += ntohs(hdr->header.size);
312     if ( (bytes_sent_after_switch > 0) &&
313          (bytes_recv_after_switch > 0) )
314     {
315       /* A peer switched addresses and sent and received data after the
316        * switch operations */
317       GNUNET_SCHEDULER_shutdown ();
318     }
319   }
320 }
321
322
323 static void
324 notify_send (void *cls)
325 {
326   static uint32_t cnt;
327
328   GNUNET_assert (GNUNET_OK ==
329                  GNUNET_TRANSPORT_TESTING_send (ccc->p[1],
330                                                 ccc->p[0],
331                                                 GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE,
332                                                 GNUNET_TRANSPORT_TESTING_LARGE_MESSAGE_SIZE,
333                                                 ++cnt,
334                                                 &notify_send,
335                                                 NULL));
336   if ( ( (stats[0].switch_attempts >= 1) ||
337          (stats[1].switch_attempts >= 1) ) &&
338        (stats[0].switch_attempts == stats[0].switch_fail + stats[0].switch_success) &&
339        (stats[1].switch_attempts == stats[1].switch_fail + stats[1].switch_success) )
340   {
341     bytes_sent_after_switch
342       += GNUNET_TRANSPORT_TESTING_LARGE_MESSAGE_SIZE;
343   }
344 }
345
346
347 static void
348 progress_indicator (void *cls)
349 {
350   static int counter;
351
352   measure_task = NULL;
353   counter++;
354   if ((TIMEOUT.rel_value_us / 1000 / 1000LL) < counter)
355   {
356     FPRINTF (stderr, "%s", ".\n");
357   }
358   else
359   {
360     FPRINTF (stderr, "%s", ".");
361     measure_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
362                                                  &progress_indicator,
363                                                  NULL);
364   }
365 }
366
367
368 static void
369 connected_cb (void *cls)
370 {
371   for (unsigned int i=0;i<2;i++)
372   {
373     stats[i].stat = GNUNET_STATISTICS_create ("transport",
374                                               ccc->p[i]->cfg);
375     if (NULL == stats[i].stat)
376     {
377       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
378                   "Fail! Could not create statistics for peers!\n");
379       ccc->global_ret = GNUNET_SYSERR;
380       GNUNET_SCHEDULER_shutdown ();
381       return;
382     }
383     for (unsigned int j=0;NULL != watches[j].stat_name; j++)
384     {
385       GNUNET_STATISTICS_watch (stats[i].stat,
386                                "transport",
387                                watches[j].stat_name,
388                                watches[j].stat_handler,
389                                &stats[i]);
390     }
391   }
392   /* Show progress */
393   ccc->global_ret = GNUNET_OK;
394   measure_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
395                                                &progress_indicator,
396                                                NULL);
397   /* Peers are connected, start transmit test messages */
398   GNUNET_assert (GNUNET_OK ==
399                  GNUNET_TRANSPORT_TESTING_send (ccc->p[1],
400                                                 ccc->p[0],
401                                                 GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE,
402                                                 GNUNET_TRANSPORT_TESTING_LARGE_MESSAGE_SIZE,
403                                                 0,
404                                                 &notify_send,
405                                                 NULL));
406 }
407
408
409 int
410 main (int argc,
411       char *argv[])
412 {
413   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext my_ccc = {
414     .connect_continuation = &connected_cb,
415     .config_file = "test_transport_api_data.conf",
416     .rec = &notify_receive,
417     .nc = &GNUNET_TRANSPORT_TESTING_log_connect,
418     .shutdown_task = &custom_shutdown,
419     .timeout = TIMEOUT
420   };
421   ccc = &my_ccc;
422   int ret;
423
424   ret = GNUNET_TRANSPORT_TESTING_main (2,
425                                        &GNUNET_TRANSPORT_TESTING_connect_check,
426                                        ccc);
427   if (77 == ret)
428     return 77;
429   if (GNUNET_OK != ret)
430     return 1;
431   return 0;
432 }
433 /* end of test_transport_address_switch.c */