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