moving code to generate cfg name used in every test to testing lib
[oweals/gnunet.git] / src / transport / test_transport_api_unreliability.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010 Christian Grothoff (and other contributing authors)
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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file transport/test_transport_api_unreliability.c
22  * @brief test case for transports; ensures messages get
23  *        through, regardless of order
24  *
25  * This test case serves as a base for unreliable
26  * transport test cases to check that the transports
27  * achieve reliable message delivery.
28  */
29 #include "platform.h"
30 #include "gnunet_common.h"
31 #include "gnunet_hello_lib.h"
32 #include "gnunet_getopt_lib.h"
33 #include "gnunet_os_lib.h"
34 #include "gnunet_program_lib.h"
35 #include "gnunet_scheduler_lib.h"
36 #include "gnunet_server_lib.h"
37 #include "gnunet_transport_service.h"
38 #include "gauger.h"
39 #include "transport.h"
40 #include "transport-testing.h"
41
42 #define VERBOSE GNUNET_NO
43
44 #define VERBOSE_ARM GNUNET_NO
45
46 #define START_ARM GNUNET_YES
47
48 /**
49  * How long until we give up on transmitting the message?
50  */
51 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 900)
52
53 static int ok;
54
55 static GNUNET_SCHEDULER_TaskIdentifier die_task;
56
57 struct PeerContext *p1;
58
59 struct PeerContext *p2;
60
61 struct GNUNET_TRANSPORT_TransmitHandle *th;
62
63 char *cfg_file_p1;
64
65 char *cfg_file_p2;
66
67 /*
68  * Testcase specific declarations
69  */
70
71 /**
72  * Note that this value must not significantly exceed
73  * 'MAX_PENDING' in 'gnunet-service-transport.c', otherwise
74  * messages may be dropped even for a reliable transport.
75  */
76 #define TOTAL_MSGS (1024 * 3)
77
78 #define MTYPE 12345
79
80 struct TestMessage
81 {
82   struct GNUNET_MessageHeader header;
83   uint32_t num;
84 };
85
86 static char *test_name;
87
88 static int msg_scheduled;
89 static int msg_sent;
90 static int msg_recv_expected;
91 static int msg_recv;
92
93 static int test_failed;
94
95 static unsigned long long total_bytes;
96
97 static struct GNUNET_TIME_Absolute start_time;
98
99 static char bitmap[TOTAL_MSGS / 8];
100
101 /*
102  * END Testcase specific declarations
103  */
104
105 #if VERBOSE
106 #define OKPP do { ok++; fprintf (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0)
107 #else
108 #define OKPP do { ok++; } while (0)
109 #endif
110
111 int
112 get_bit (const char *map, unsigned int bit);
113
114 static void
115 end ()
116 {
117   unsigned long long delta;
118
119   char *value_name;
120
121   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Stopping peers\n");
122
123   delta = GNUNET_TIME_absolute_get_duration (start_time).rel_value;
124   fprintf (stderr, "\nThroughput was %llu kb/s\n",
125            total_bytes * 1000 / 1024 / delta);
126   GNUNET_asprintf (&value_name, "unreliable_%s", test_name);
127   GAUGER ("TRANSPORT", value_name, (int) (total_bytes * 1000 / 1024 / delta),
128           "kb/s");
129   GNUNET_free (value_name);
130
131   if (die_task != GNUNET_SCHEDULER_NO_TASK)
132     GNUNET_SCHEDULER_cancel (die_task);
133
134   if (th != NULL)
135     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
136   th = NULL;
137
138   GNUNET_TRANSPORT_TESTING_stop_peer (p1);
139   GNUNET_TRANSPORT_TESTING_stop_peer (p2);
140
141   ok = 0;
142
143   int i;
144
145   for (i = 0; i < TOTAL_MSGS; i++)
146   {
147     if (get_bit (bitmap, i) == 0)
148     {
149       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Did not receive message %d\n", i);
150       ok = -1;
151     }
152   }
153 }
154
155 static void
156 end_badly ()
157 {
158   die_task = GNUNET_SCHEDULER_NO_TASK;
159   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Fail! Stopping peers\n");
160
161   if (test_failed == GNUNET_NO)
162     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Testcase timeout\n");
163   else
164     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
165                 "Reliability failed: Last message sent %u, Next message scheduled %u, Last message received %u, Message expected %u\n",
166                 msg_sent, msg_scheduled, msg_recv, msg_recv_expected);
167
168   if (th != NULL)
169     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
170   th = NULL;
171
172   if (p1 != NULL)
173     GNUNET_TRANSPORT_TESTING_stop_peer (p1);
174   if (p2 != NULL)
175     GNUNET_TRANSPORT_TESTING_stop_peer (p2);
176
177   ok = GNUNET_SYSERR;
178 }
179
180
181 static unsigned int
182 get_size (unsigned int iter)
183 {
184   unsigned int ret;
185
186   ret = (iter * iter * iter);
187   return sizeof (struct TestMessage) + (ret % 60000);
188 }
189
190
191 /**
192  * Sets a bit active in the bitmap.
193  *
194  * @param bitIdx which bit to set
195  */
196 static void
197 set_bit (unsigned int bitIdx)
198 {
199   size_t arraySlot;
200   unsigned int targetBit;
201
202   if (bitIdx >= sizeof (bitmap) * 8)
203   {
204     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "tried to set bit %d of %d(!?!?)\n",
205                 bitIdx, sizeof (bitmap) * 8);
206     return;
207   }
208   arraySlot = bitIdx / 8;
209   targetBit = (1L << (bitIdx % 8));
210   bitmap[arraySlot] |= targetBit;
211 }
212
213 /**
214  * Obtain a bit from bitmap.
215  * @param map the bitmap
216  * @param bit index from bitmap
217  *
218  * @return Bit \a bit from hashcode \a code
219  */
220 int
221 get_bit (const char *map, unsigned int bit)
222 {
223   if (bit >= TOTAL_MSGS)
224   {
225     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "get bit %d of %d(!?!?)\n", bit,
226                 sizeof (bitmap) * 8);
227     return 0;
228   }
229   return ((map)[bit >> 3] & (1 << (bit & 7))) > 0;
230 }
231
232
233 static void
234 notify_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
235                 const struct GNUNET_MessageHeader *message,
236                 const struct GNUNET_TRANSPORT_ATS_Information *ats,
237                 uint32_t ats_count)
238 {
239   static int n;
240
241   unsigned int s;
242   char cbuf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
243   const struct TestMessage *hdr;
244
245   hdr = (const struct TestMessage *) message;
246
247   if (MTYPE != ntohs (message->type))
248     return;
249   msg_recv_expected = n;
250   msg_recv = ntohl (hdr->num);
251   s = get_size (ntohl (hdr->num));
252
253   if (ntohs (message->size) != s)
254   {
255     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
256                 "Expected message %u of size %u, got %u bytes of message %u\n",
257                 ntohl (hdr->num), s, ntohs (message->size), ntohl (hdr->num));
258     if (GNUNET_SCHEDULER_NO_TASK != die_task)
259       GNUNET_SCHEDULER_cancel (die_task);
260     test_failed = GNUNET_YES;
261     die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
262     return;
263   }
264
265   memset (cbuf, ntohl (hdr->num), s - sizeof (struct TestMessage));
266   if (0 != memcmp (cbuf, &hdr[1], s - sizeof (struct TestMessage)))
267   {
268     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
269                 "Expected message %u with bits %u, but body did not match\n",
270                 ntohl (hdr->num), (unsigned char) n);
271     if (GNUNET_SCHEDULER_NO_TASK != die_task)
272       GNUNET_SCHEDULER_cancel (die_task);
273     test_failed = GNUNET_YES;
274     die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
275     return;
276   }
277 #if VERBOSE
278   if (ntohl (hdr->num) % 5 == 0)
279   {
280     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got message %u of size %u\n",
281                 ntohl (hdr->num), ntohs (message->size));
282   }
283 #endif
284   n++;
285   set_bit (ntohl (hdr->num));
286   if (0 == (n % (TOTAL_MSGS / 100)))
287   {
288     fprintf (stderr, ".");
289     if (GNUNET_SCHEDULER_NO_TASK != die_task)
290       GNUNET_SCHEDULER_cancel (die_task);
291     test_failed = GNUNET_YES;
292     die_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
293   }
294   if (n == TOTAL_MSGS)
295     end ();
296 }
297
298
299 static size_t
300 notify_ready (void *cls, size_t size, void *buf)
301 {
302   static int n;
303   char *cbuf = buf;
304   struct TestMessage hdr;
305   unsigned int s;
306   unsigned int ret;
307
308   th = NULL;
309
310   if (buf == NULL)
311   {
312     GNUNET_break (0);
313     ok = 42;
314     return 0;
315   }
316   ret = 0;
317   s = get_size (n);
318   GNUNET_assert (size >= s);
319   GNUNET_assert (buf != NULL);
320   cbuf = buf;
321   do
322   {
323     hdr.header.size = htons (s);
324     hdr.header.type = htons (MTYPE);
325     hdr.num = htonl (n);
326     msg_sent = n;
327     memcpy (&cbuf[ret], &hdr, sizeof (struct TestMessage));
328     ret += sizeof (struct TestMessage);
329     memset (&cbuf[ret], n, s - sizeof (struct TestMessage));
330     ret += s - sizeof (struct TestMessage);
331 #if VERBOSE
332     if (n % 5000 == 0)
333     {
334       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending message %u of size %u\n", n,
335                   s);
336     }
337
338 #endif
339     n++;
340     s = get_size (n);
341     if (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 16))
342       break;                    /* sometimes pack buffer full, sometimes not */
343   }
344   while (size - ret >= s);
345   if (n < TOTAL_MSGS)
346   {
347     th = GNUNET_TRANSPORT_notify_transmit_ready (p2->th, &p1->id, s, 0, TIMEOUT,
348                                                  &notify_ready, NULL);
349     msg_scheduled = n;
350   }
351   else
352   {
353     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
354                 "All messages scheduled to be sent!!\n");
355     if (GNUNET_SCHEDULER_NO_TASK != die_task)
356       GNUNET_SCHEDULER_cancel (die_task);
357     die_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
358   }
359   if (n % 5000 == 0)
360   {
361     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
362                 "Returning total message block of size %u\n", ret);
363   }
364   total_bytes += ret;
365   return ret;
366 }
367
368
369 static void
370 notify_connect (void *cls, const struct GNUNET_PeerIdentity *peer,
371                 const struct GNUNET_TRANSPORT_ATS_Information *ats,
372                 uint32_t ats_count)
373 {
374
375   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%4s' connected to us (%p)!\n",
376               GNUNET_i2s (peer), cls);
377
378   if (cls == p1)
379   {
380     GNUNET_TRANSPORT_set_quota (p1->th, &p2->id,
381                                 GNUNET_BANDWIDTH_value_init (1024 * 1024 *
382                                                              1024),
383                                 GNUNET_BANDWIDTH_value_init (1024 * 1024 *
384                                                              1024));
385   }
386   else if (cls == p2)
387   {
388     GNUNET_TRANSPORT_set_quota (p2->th, &p1->id,
389                                 GNUNET_BANDWIDTH_value_init (1024 * 1024 *
390                                                              1024),
391                                 GNUNET_BANDWIDTH_value_init (1024 * 1024 *
392                                                              1024));
393   }
394 }
395
396
397 static void
398 notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
399 {
400   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%4s' disconnected (%p)!\n",
401               GNUNET_i2s (peer), cls);
402 }
403
404 static void
405 sendtask ()
406 {
407   start_time = GNUNET_TIME_absolute_get ();
408   th = GNUNET_TRANSPORT_notify_transmit_ready (p2->th, &p1->id, get_size (0), 0,
409                                                TIMEOUT, &notify_ready, NULL);
410 }
411
412 static void
413 testing_connect_cb (struct PeerContext *p1, struct PeerContext *p2, void *cls)
414 {
415   char *p1_c = strdup (GNUNET_i2s (&p1->id));
416
417   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peers connected: %s <-> %s\n", p1_c,
418               GNUNET_i2s (&p2->id));
419   GNUNET_free (p1_c);
420
421   // FIXME: THIS IS REQUIRED! SEEMS TO BE A BUG!
422   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &sendtask, NULL);
423 }
424
425 static void
426 run (void *cls, char *const *args, const char *cfgfile,
427      const struct GNUNET_CONFIGURATION_Handle *cfg)
428 {
429   die_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
430
431   p1 = GNUNET_TRANSPORT_TESTING_start_peer (cfg_file_p1, &notify_receive,
432                                             &notify_connect, &notify_disconnect,
433                                             NULL);
434   p2 = GNUNET_TRANSPORT_TESTING_start_peer (cfg_file_p2, &notify_receive,
435                                             &notify_connect, &notify_disconnect,
436                                             NULL);
437
438   GNUNET_TRANSPORT_TESTING_connect_peers (p1, p2, &testing_connect_cb, NULL);
439 }
440
441 static int
442 check ()
443 {
444   static char *const argv[] = { "test-transport-api-unreliability",
445     "-c",
446     "test_transport_api_data.conf",
447 #if VERBOSE
448     "-L", "DEBUG",
449 #endif
450     NULL
451   };
452   static struct GNUNET_GETOPT_CommandLineOption options[] = {
453     GNUNET_GETOPT_OPTION_END
454   };
455
456 #if WRITECONFIG
457   setTransportOptions ("test_transport_api_data.conf");
458 #endif
459   ok = GNUNET_SYSERR;
460
461   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv,
462                       "test-transport-api-unreliability", "nohelp", options,
463                       &run, &ok);
464
465   return ok;
466 }
467
468 int
469 main (int argc, char *argv[])
470 {
471   int ret;
472   int nat_res;
473
474   GNUNET_log_setup ("test-transport-api-unreliability",
475 #if VERBOSE
476                     "DEBUG",
477 #else
478                     "WARNING",
479 #endif
480                     NULL);
481
482   char *pch = strdup (argv[0]);
483   char *backup = pch;
484   char *filename = NULL;
485   char *dotexe;
486   char *src_name = strdup (__FILE__);
487   char *split = NULL;
488
489   /* get executable filename */
490   pch = strtok (pch, "/");
491   while (pch != NULL)
492   {
493     pch = strtok (NULL, "/");
494     if (pch != NULL)
495       filename = pch;
496   }
497   /* remove "lt-" */
498   filename = strstr (filename, "tes");
499   if (NULL != (dotexe = strstr (filename, ".exe")))
500     dotexe[0] = '\0';
501
502   split = strstr (src_name, ".");
503   if (split != NULL)
504   {
505     split[0] = '\0';
506     test_name = strdup (&filename[strlen (src_name) + 1]);
507   }
508   else
509     test_name = NULL;
510
511   GNUNET_free (src_name);
512   GNUNET_free (backup);
513
514   if ((strstr (argv[0], "tcp_nat") != NULL) ||
515       (strstr (argv[0], "udp_nat") != NULL))
516   {
517     nat_res = GNUNET_OS_check_helper_binary ("gnunet-nat-server");
518     if (GNUNET_NO == nat_res)
519     {
520       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Cannot run NAT test: `%s' %s \n",
521                   "gnunet-nat-server", "SUID not set");
522       return 0;
523     }
524     if (GNUNET_SYSERR == nat_res)
525     {
526       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Cannot run NAT test: `%s' %s \n",
527                   "gnunet-nat-server", "file not found");
528       return 0;
529     }
530   }
531
532   GNUNET_TRANSPORT_TESTING_get_config_name (argv[0], &cfg_file_p1, 1);
533   GNUNET_TRANSPORT_TESTING_get_config_name (argv[0], &cfg_file_p2, 2);
534
535   ret = check ();
536
537   GNUNET_free (cfg_file_p1);
538   GNUNET_free (cfg_file_p2);
539
540   GNUNET_free_non_null (test_name);
541
542   return ret;
543 }
544
545 /* end of test_transport_api_unreliability.c */