-simplify logic
[oweals/gnunet.git] / src / transport / test_transport_api_reliability.c
1 /*
2      This file is part of GNUnet.
3      Copyright (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_reliability.c
22  * @brief base test case for transport implementations
23  *
24  * This test case serves ensures that messages are reliably sent between peers
25  *
26  * This test sends TOTAL_MSGS with message type MTYPE from peer 1 to peer 2
27  * and ensures that all message were received.
28  */
29 #include "platform.h"
30 #include "gnunet_transport_service.h"
31 #include "gauger.h"
32 #include "transport-testing.h"
33
34 /**
35  * Total number of messages to send
36  *
37  * Note that this value must not significantly exceed
38  * 'MAX_PENDING' in 'gnunet-service-transport_clients.c', otherwise
39  * messages may be dropped even for a reliable transport.
40  */
41 #define TOTAL_MSGS (1024 * 3)
42
43 /**
44  * Message type of test messages
45  */
46 #define MTYPE 12345
47
48 /**
49  * Testcase timeout
50  */
51 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 90)
52
53 /**
54  * How long until we give up on transmitting the message?
55  */
56 #define TIMEOUT_TRANSMIT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
57
58
59 /**
60  * Struct for the test message
61  */
62 GNUNET_NETWORK_STRUCT_BEGIN
63
64 struct TestMessage
65 {
66   struct GNUNET_MessageHeader header;
67   uint32_t num;
68 };
69 GNUNET_NETWORK_STRUCT_END
70
71
72 /**
73  * Name of the plugin to test
74  */
75 static char *test_plugin;
76
77 /**
78  * Name of the test
79  */
80 static char *test_name;
81
82 /**
83  * Return value of the test
84  */
85 static int ok;
86
87 /**
88  * Context of peer 1
89  */
90 static struct PeerContext *p1;
91
92 /**
93  * Configuration file of peer 1
94  */
95 static char *cfg_file_p1;
96
97 /**
98  * Context of peer 2
99  */
100 static struct PeerContext *p2;
101
102 /**
103  * Configuration file of peer 1
104  */
105 static char *cfg_file_p2;
106
107 /**
108  * Timeout task
109  */
110 static struct GNUNET_SCHEDULER_Task * die_task;
111
112 /**
113  * Transport transmit handle used
114  */
115 static struct GNUNET_TRANSPORT_TransmitHandle *th;
116
117 /**
118  * Transport testing handle
119  */
120 static struct GNUNET_TRANSPORT_TESTING_handle *tth;
121
122 /*
123  * Total amount of bytes sent
124  */
125 static unsigned long long total_bytes;
126
127 /**
128  * Time of start
129  */
130 static struct GNUNET_TIME_Absolute start_time;
131
132 /**
133  * No. of message currently scheduled to be send
134  */
135 static int msg_scheduled;
136
137 /**
138  * No. of last message sent
139  */
140 static int msg_sent;
141
142 /**
143  * No. of last message received
144  */
145 static int msg_recv;
146
147 static int test_connected;
148
149 static int test_sending;
150
151 static int test_send_timeout;
152
153
154 /**
155  * Bitmap storing which messages were received
156  */
157
158 static char bitmap[TOTAL_MSGS / 8];
159
160 static GNUNET_TRANSPORT_TESTING_ConnectRequest cc;
161
162 /*
163  * END Testcase specific declarations
164  */
165
166 #if VERBOSE
167 #define OKPP do { ok++; FPRINTF (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0)
168 #else
169 #define OKPP do { ok++; } while (0)
170 #endif
171
172
173 static int
174 get_bit (const char *map, unsigned int bit);
175
176
177 static void
178 end ()
179 {
180   unsigned long long delta;
181   unsigned long long rate;
182   char *value_name;
183
184   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Stopping peers\n");
185
186   /* Calculcate statistics   */
187   delta = GNUNET_TIME_absolute_get_duration (start_time).rel_value_us;
188   rate = (1000LL* 1000ll * total_bytes) / (1024 * delta);
189   FPRINTF (stderr, "\nThroughput was %llu KiBytes/s\n",
190       rate);
191
192   GNUNET_asprintf (&value_name, "unreliable_%s", test_plugin);
193   GAUGER ("TRANSPORT", value_name, (int) rate,
194           "kb/s");
195   GNUNET_free (value_name);
196
197   if (die_task != NULL)
198     GNUNET_SCHEDULER_cancel (die_task);
199
200   if (th != NULL)
201     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
202   th = NULL;
203
204   if (cc != NULL)
205     GNUNET_TRANSPORT_TESTING_connect_peers_cancel (tth, cc);
206   cc = NULL;
207
208   GNUNET_TRANSPORT_TESTING_stop_peer (tth, p1);
209   GNUNET_TRANSPORT_TESTING_stop_peer (tth, p2);
210
211   GNUNET_TRANSPORT_TESTING_done (tth);
212
213   ok = 0;
214
215   int i;
216
217   for (i = 0; i < TOTAL_MSGS; i++)
218   {
219     if (get_bit (bitmap, i) == 0)
220     {
221       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Did not receive message %d\n", i);
222       ok = -1;
223     }
224   }
225 }
226
227 static void
228 end_badly ()
229 {
230   unsigned int i;
231
232   die_task = NULL;
233   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
234               "Fail! Stopping peers\n");
235   if (test_connected == GNUNET_YES)
236     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
237                 "Peers got connected\n");
238   else
239     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
240                 "Peers got NOT connected\n");
241
242   if (test_sending == GNUNET_NO)
243     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
244                 "Testcase did not send any messages before timeout\n");
245   if (test_send_timeout == GNUNET_YES)
246     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
247                 "Test had timeout while waiting to send data\n");
248   for (i = 0; i < TOTAL_MSGS; i++)
249   {
250     if (get_bit (bitmap, i) == 0)
251     {
252       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
253                   "Did not receive message %u\n",
254                   i);
255       ok = -1;
256     }
257   }
258
259   if (th != NULL)
260     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
261   th = NULL;
262
263   if (cc != NULL)
264     GNUNET_TRANSPORT_TESTING_connect_peers_cancel (tth, cc);
265   cc = NULL;
266
267   if (p1 != NULL)
268     GNUNET_TRANSPORT_TESTING_stop_peer (tth, p1);
269   if (p2 != NULL)
270     GNUNET_TRANSPORT_TESTING_stop_peer (tth, p2);
271   GNUNET_TRANSPORT_TESTING_done (tth);
272   ok = GNUNET_SYSERR;
273 }
274
275
276 static unsigned int
277 get_size (unsigned int iter)
278 {
279   unsigned int ret;
280
281   ret = (iter * iter * iter);
282
283 #ifndef LINUX
284   /* FreeBSD/OSX etc. Unix DGRAMs do not work
285    * with large messages */
286   if (0 == strcmp ("unix", test_plugin))
287     return sizeof (struct TestMessage) + (ret % 1024);
288 #endif
289   return sizeof (struct TestMessage) + (ret % 60000);
290 }
291
292
293 /**
294  * Sets a bit active in the bitmap.
295  *
296  * @param bitIdx which bit to set
297  * @return GNUNET_SYSERR on error, GNUNET_OK on success
298  */
299 static int
300 set_bit (unsigned int bitIdx)
301 {
302   size_t arraySlot;
303   unsigned int targetBit;
304
305   if (bitIdx >= sizeof (bitmap) * 8)
306   {
307     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "tried to set bit %d of %d(!?!?)\n",
308                 bitIdx, sizeof (bitmap) * 8);
309     return GNUNET_SYSERR;
310   }
311   arraySlot = bitIdx / 8;
312   targetBit = (1L << (bitIdx % 8));
313   bitmap[arraySlot] |= targetBit;
314   return GNUNET_OK;
315 }
316
317 /**
318  * Obtain a bit from bitmap.
319  * @param map the bitmap
320  * @param bit index from bitmap
321  *
322  * @return Bit \a bit from hashcode \a code
323  */
324 static int
325 get_bit (const char *map, unsigned int bit)
326 {
327   if (bit > TOTAL_MSGS)
328   {
329     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "get bit %d of %d(!?!?)\n", bit,
330                 sizeof (bitmap) * 8);
331     return 0;
332   }
333   return ((map)[bit >> 3] & (1 << (bit & 7))) > 0;
334 }
335
336
337 static void
338 notify_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
339                 const struct GNUNET_MessageHeader *message)
340 {
341   static int n;
342
343   unsigned int s;
344   char cbuf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
345   const struct TestMessage *hdr;
346
347   hdr = (const struct TestMessage *) message;
348
349   if (MTYPE != ntohs (message->type))
350     return;
351   msg_recv = ntohl (hdr->num);
352   s = get_size (ntohl (hdr->num));
353
354   if (ntohs (message->size) != s)
355   {
356     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
357                 "Expected message %u of size %u, got %u bytes of message %u\n",
358                 ntohl (hdr->num), s, ntohs (message->size), ntohl (hdr->num));
359     if (NULL != die_task)
360       GNUNET_SCHEDULER_cancel (die_task);
361     test_sending = GNUNET_YES;
362     die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
363     return;
364   }
365
366   memset (cbuf, ntohl (hdr->num), s - sizeof (struct TestMessage));
367   if (0 != memcmp (cbuf, &hdr[1], s - sizeof (struct TestMessage)))
368   {
369     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
370                 "Expected message %u with bits %u, but body did not match\n",
371                 ntohl (hdr->num), (unsigned char) n);
372     if (NULL != die_task)
373       GNUNET_SCHEDULER_cancel (die_task);
374     test_sending = GNUNET_YES;
375     die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
376     return;
377   }
378 #if VERBOSE
379   if (ntohl (hdr->num) % 5 == 0)
380   {
381     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got message %u of size %u\n",
382                 ntohl (hdr->num), ntohs (message->size));
383   }
384 #endif
385   n++;
386   if (GNUNET_SYSERR == set_bit (ntohl (hdr->num)))
387   {
388       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
389                   _("Message id %u is bigger than maxmimum number of messages %u expected\n"),
390                   ntohl (hdr->num), TOTAL_MSGS);
391   }
392   test_sending = GNUNET_YES;
393   if (0 == (n % (TOTAL_MSGS / 100)))
394   {
395     FPRINTF (stderr, "%s",  ".");
396     if (NULL != die_task)
397       GNUNET_SCHEDULER_cancel (die_task);
398     die_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
399   }
400   if (n == TOTAL_MSGS)
401   {
402     end ();
403   }
404 }
405
406
407 static size_t
408 notify_ready (void *cls, size_t size, void *buf)
409 {
410   static int n;
411   char *cbuf = buf;
412   struct TestMessage hdr;
413   unsigned int s;
414   unsigned int ret;
415
416   th = NULL;
417
418   if (buf == NULL)
419   {
420     test_send_timeout = GNUNET_YES;
421     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
422                 "Timeout occurred while waiting for transmit_ready for msg %u of %u\n",
423                 msg_scheduled, TOTAL_MSGS);
424     if (NULL != die_task)
425       GNUNET_SCHEDULER_cancel (die_task);
426     die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
427     ok = 42;
428     return 0;
429   }
430   ret = 0;
431   s = get_size (n);
432   GNUNET_assert (size >= s);
433   GNUNET_assert (buf != NULL);
434   GNUNET_assert (n < TOTAL_MSGS);
435   cbuf = buf;
436   do
437   {
438     GNUNET_assert (n < TOTAL_MSGS);
439     hdr.header.size = htons (s);
440     hdr.header.type = htons (MTYPE);
441     hdr.num = htonl (n);
442     msg_sent = n;
443     memcpy (&cbuf[ret], &hdr, sizeof (struct TestMessage));
444     ret += sizeof (struct TestMessage);
445     memset (&cbuf[ret], n, s - sizeof (struct TestMessage));
446     ret += s - sizeof (struct TestMessage);
447
448 #if VERBOSE
449     if (n % 5000 == 0)
450     {
451       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending message %u of size %u\n", n,
452                   s);
453     }
454 #endif
455     n++;
456     s = get_size (n);
457     if (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 16))
458       break;                    /* sometimes pack buffer full, sometimes not */
459   }
460   while ((size - ret >= s) && (n < TOTAL_MSGS));
461   if (n < TOTAL_MSGS)
462   {
463     th = GNUNET_TRANSPORT_notify_transmit_ready (p2->th, &p1->id, s,
464                                                  TIMEOUT_TRANSMIT,
465                                                  &notify_ready, NULL);
466     msg_scheduled = n;
467   }
468   else
469   {
470     FPRINTF (stderr, "%s",  "\n");
471     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "All messages scheduled to be sent\n");
472     if (NULL != die_task)
473       GNUNET_SCHEDULER_cancel (die_task);
474     die_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
475   }
476   if (n % 5000 == 0)
477   {
478     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
479                 "Returning total message block of size %u\n", ret);
480   }
481   total_bytes += ret;
482   return ret;
483 }
484
485
486 static void
487 notify_connect (void *cls, const struct GNUNET_PeerIdentity *peer)
488 {
489   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%4s' connected to us (%p)!\n",
490               GNUNET_i2s (peer), cls);
491 }
492
493
494 static void
495 notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
496 {
497   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%4s' disconnected (%p)!\n",
498               GNUNET_i2s (peer), cls);
499   if (th != NULL)
500     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
501   th = NULL;
502 }
503
504 static void
505 sendtask ()
506 {
507   start_time = GNUNET_TIME_absolute_get ();
508   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting to send %u messages\n",
509               TOTAL_MSGS);
510   th = GNUNET_TRANSPORT_notify_transmit_ready (p2->th, &p1->id, get_size (0),
511                                                TIMEOUT_TRANSMIT, &notify_ready,
512                                                NULL);
513 }
514
515 static void
516 testing_connect_cb (struct PeerContext *p1, struct PeerContext *p2, void *cls)
517 {
518   char *p1_c = GNUNET_strdup (GNUNET_i2s (&p1->id));
519
520   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peers connected: %s <-> %s\n", p1_c,
521               GNUNET_i2s (&p2->id));
522   GNUNET_free (p1_c);
523
524   test_connected = GNUNET_YES;
525   cc = NULL;
526
527   GNUNET_SCHEDULER_add_now (&sendtask, NULL);
528 }
529
530 static void
531 start_cb (struct PeerContext *p, void *cls)
532 {
533   static int started;
534   started++;
535
536   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer %u (`%s') started\n", p->no,
537               GNUNET_i2s (&p->id));
538
539   if (started != 2)
540     return;
541
542   test_connected = GNUNET_NO;
543   cc = GNUNET_TRANSPORT_TESTING_connect_peers (tth, p1, p2, &testing_connect_cb,
544                                                NULL);
545
546 }
547
548 static void
549 run (void *cls, char *const *args, const char *cfgfile,
550      const struct GNUNET_CONFIGURATION_Handle *cfg)
551 {
552   die_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
553   test_send_timeout = GNUNET_NO;
554
555   p1 = GNUNET_TRANSPORT_TESTING_start_peer (tth, cfg_file_p1, 1,
556                                             &notify_receive, &notify_connect,
557                                             &notify_disconnect, &start_cb,
558                                             NULL);
559   p2 = GNUNET_TRANSPORT_TESTING_start_peer (tth, cfg_file_p2, 2,
560                                             &notify_receive, &notify_connect,
561                                             &notify_disconnect, &start_cb,
562                                             NULL);
563   if ((p1 == NULL) || (p2 == NULL))
564   {
565     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Fail! Could not start peers!\n");
566     if (die_task != NULL)
567       GNUNET_SCHEDULER_cancel (die_task);
568     //die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
569     return;
570   }
571 }
572
573
574 int
575 main (int argc, char *argv[])
576 {
577   char *test_source;
578   int ret;
579
580   static char *const argv_new[] = { "test-transport-api-reliability",
581     "-c",
582     "test_transport_api_data.conf",
583     NULL
584   };
585   static struct GNUNET_GETOPT_CommandLineOption options[] = {
586     GNUNET_GETOPT_OPTION_END
587   };
588
589   GNUNET_TRANSPORT_TESTING_get_test_name (argv[0], &test_name);
590
591   GNUNET_log_setup (test_name,
592                     "WARNING",
593                     NULL);
594
595   GNUNET_TRANSPORT_TESTING_get_test_source_name (__FILE__, &test_source);
596   GNUNET_TRANSPORT_TESTING_get_test_plugin_name (argv[0], test_source,
597                                                  &test_plugin);
598
599   tth = GNUNET_TRANSPORT_TESTING_init ();
600
601   GNUNET_TRANSPORT_TESTING_get_config_name (argv[0], &cfg_file_p1, 1);
602   GNUNET_TRANSPORT_TESTING_get_config_name (argv[0], &cfg_file_p2, 2);
603
604
605
606 #if WRITECONFIG
607   setTransportOptions ("test_transport_api_data.conf");
608 #endif
609   ok = GNUNET_SYSERR;
610
611   ret = GNUNET_PROGRAM_run ((sizeof (argv_new) / sizeof (char *)) - 1, argv_new, test_name,
612                       "nohelp", options, &run, &ok);
613   if (GNUNET_SYSERR == ret)
614     ok = -1;
615
616   GNUNET_free (cfg_file_p1);
617   GNUNET_free (cfg_file_p2);
618
619   GNUNET_free (test_source);
620   GNUNET_free (test_plugin);
621   GNUNET_free (test_name);
622
623   return ok;
624 }
625
626 /* end of test_transport_api_reliability.c */