document API, do not pass unused 'session' argument
[oweals/gnunet.git] / src / transport / test_transport_api_reliability.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_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, 900)
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   int i;
231   die_task = NULL;
232   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Fail! Stopping peers\n");
233
234   if (test_connected == GNUNET_YES)
235     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Peers got connected\n");
236   else
237     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Peers got NOT connected\n");
238
239   if (test_sending == GNUNET_NO)
240     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
241                 "Testcase did not send any messages before timeout\n");
242   if (test_send_timeout == GNUNET_YES)
243     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
244                 "Test had timeout while waiting to send data\n");
245   for (i = 0; i < TOTAL_MSGS; i++)
246   {
247     if (get_bit (bitmap, i) == 0)
248     {
249       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Did not receive message %d\n", i);
250       ok = -1;
251     }
252   }
253
254   if (th != NULL)
255     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
256   th = NULL;
257
258   if (cc != NULL)
259     GNUNET_TRANSPORT_TESTING_connect_peers_cancel (tth, cc);
260   cc = NULL;
261
262   if (p1 != NULL)
263     GNUNET_TRANSPORT_TESTING_stop_peer (tth, p1);
264   if (p2 != NULL)
265     GNUNET_TRANSPORT_TESTING_stop_peer (tth, p2);
266   GNUNET_TRANSPORT_TESTING_done (tth);
267   ok = GNUNET_SYSERR;
268 }
269
270
271 static unsigned int
272 get_size (unsigned int iter)
273 {
274   unsigned int ret;
275
276   ret = (iter * iter * iter);
277
278 #ifndef LINUX
279   /* FreeBSD/OSX etc. Unix DGRAMs do not work
280    * with large messages */
281   if (0 == strcmp ("unix", test_plugin))
282     return sizeof (struct TestMessage) + (ret % 1024);
283 #endif
284   return sizeof (struct TestMessage) + (ret % 60000);
285 }
286
287
288 /**
289  * Sets a bit active in the bitmap.
290  *
291  * @param bitIdx which bit to set
292  * @return GNUNET_SYSERR on error, GNUNET_OK on success
293  */
294 static int
295 set_bit (unsigned int bitIdx)
296 {
297   size_t arraySlot;
298   unsigned int targetBit;
299
300   if (bitIdx >= sizeof (bitmap) * 8)
301   {
302     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "tried to set bit %d of %d(!?!?)\n",
303                 bitIdx, sizeof (bitmap) * 8);
304     return GNUNET_SYSERR;
305   }
306   arraySlot = bitIdx / 8;
307   targetBit = (1L << (bitIdx % 8));
308   bitmap[arraySlot] |= targetBit;
309   return GNUNET_OK;
310 }
311
312 /**
313  * Obtain a bit from bitmap.
314  * @param map the bitmap
315  * @param bit index from bitmap
316  *
317  * @return Bit \a bit from hashcode \a code
318  */
319 static int
320 get_bit (const char *map, unsigned int bit)
321 {
322   if (bit > TOTAL_MSGS)
323   {
324     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "get bit %d of %d(!?!?)\n", bit,
325                 sizeof (bitmap) * 8);
326     return 0;
327   }
328   return ((map)[bit >> 3] & (1 << (bit & 7))) > 0;
329 }
330
331
332 static void
333 notify_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
334                 const struct GNUNET_MessageHeader *message)
335 {
336   static int n;
337
338   unsigned int s;
339   char cbuf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
340   const struct TestMessage *hdr;
341
342   hdr = (const struct TestMessage *) message;
343
344   if (MTYPE != ntohs (message->type))
345     return;
346   msg_recv = ntohl (hdr->num);
347   s = get_size (ntohl (hdr->num));
348
349   if (ntohs (message->size) != s)
350   {
351     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
352                 "Expected message %u of size %u, got %u bytes of message %u\n",
353                 ntohl (hdr->num), s, ntohs (message->size), ntohl (hdr->num));
354     if (NULL != die_task)
355       GNUNET_SCHEDULER_cancel (die_task);
356     test_sending = GNUNET_YES;
357     die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
358     return;
359   }
360
361   memset (cbuf, ntohl (hdr->num), s - sizeof (struct TestMessage));
362   if (0 != memcmp (cbuf, &hdr[1], s - sizeof (struct TestMessage)))
363   {
364     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
365                 "Expected message %u with bits %u, but body did not match\n",
366                 ntohl (hdr->num), (unsigned char) n);
367     if (NULL != die_task)
368       GNUNET_SCHEDULER_cancel (die_task);
369     test_sending = GNUNET_YES;
370     die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
371     return;
372   }
373 #if VERBOSE
374   if (ntohl (hdr->num) % 5 == 0)
375   {
376     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got message %u of size %u\n",
377                 ntohl (hdr->num), ntohs (message->size));
378   }
379 #endif
380   n++;
381   if (GNUNET_SYSERR == set_bit (ntohl (hdr->num)))
382   {
383       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
384                   _("Message id %u is bigger than maxmimum number of messages %u expected\n"),
385                   ntohl (hdr->num), TOTAL_MSGS);
386   }
387   test_sending = GNUNET_YES;
388   if (0 == (n % (TOTAL_MSGS / 100)))
389   {
390     FPRINTF (stderr, "%s",  ".");
391     if (NULL != die_task)
392       GNUNET_SCHEDULER_cancel (die_task);
393     die_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
394   }
395   if (n == TOTAL_MSGS)
396   {
397     end ();
398   }
399 }
400
401
402 static size_t
403 notify_ready (void *cls, size_t size, void *buf)
404 {
405   static int n;
406   char *cbuf = buf;
407   struct TestMessage hdr;
408   unsigned int s;
409   unsigned int ret;
410
411   th = NULL;
412
413   if (buf == NULL)
414   {
415     test_send_timeout = GNUNET_YES;
416     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
417                 "Timeout occurred while waiting for transmit_ready for msg %u of %u\n",
418                 msg_scheduled, TOTAL_MSGS);
419     if (NULL != die_task)
420       GNUNET_SCHEDULER_cancel (die_task);
421     die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
422     ok = 42;
423     return 0;
424   }
425   ret = 0;
426   s = get_size (n);
427   GNUNET_assert (size >= s);
428   GNUNET_assert (buf != NULL);
429   GNUNET_assert (n < TOTAL_MSGS);
430   cbuf = buf;
431   do
432   {
433     GNUNET_assert (n < TOTAL_MSGS);
434     hdr.header.size = htons (s);
435     hdr.header.type = htons (MTYPE);
436     hdr.num = htonl (n);
437     msg_sent = n;
438     memcpy (&cbuf[ret], &hdr, sizeof (struct TestMessage));
439     ret += sizeof (struct TestMessage);
440     memset (&cbuf[ret], n, s - sizeof (struct TestMessage));
441     ret += s - sizeof (struct TestMessage);
442
443 #if VERBOSE
444     if (n % 5000 == 0)
445     {
446       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending message %u of size %u\n", n,
447                   s);
448     }
449 #endif
450     n++;
451     s = get_size (n);
452     if (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 16))
453       break;                    /* sometimes pack buffer full, sometimes not */
454   }
455   while ((size - ret >= s) && (n < TOTAL_MSGS));
456   if (n < TOTAL_MSGS)
457   {
458     th = GNUNET_TRANSPORT_notify_transmit_ready (p2->th, &p1->id, s,
459                                                  TIMEOUT_TRANSMIT,
460                                                  &notify_ready, NULL);
461     msg_scheduled = n;
462   }
463   else
464   {
465     FPRINTF (stderr, "%s",  "\n");
466     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "All messages scheduled to be sent\n");
467     if (NULL != die_task)
468       GNUNET_SCHEDULER_cancel (die_task);
469     die_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
470   }
471   if (n % 5000 == 0)
472   {
473     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
474                 "Returning total message block of size %u\n", ret);
475   }
476   total_bytes += ret;
477   return ret;
478 }
479
480
481 static void
482 notify_connect (void *cls, const struct GNUNET_PeerIdentity *peer)
483 {
484   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%4s' connected to us (%p)!\n",
485               GNUNET_i2s (peer), cls);
486 }
487
488
489 static void
490 notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
491 {
492   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%4s' disconnected (%p)!\n",
493               GNUNET_i2s (peer), cls);
494   if (th != NULL)
495     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
496   th = NULL;
497 }
498
499 static void
500 sendtask ()
501 {
502   start_time = GNUNET_TIME_absolute_get ();
503   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting to send %u messages\n",
504               TOTAL_MSGS);
505   th = GNUNET_TRANSPORT_notify_transmit_ready (p2->th, &p1->id, get_size (0),
506                                                TIMEOUT_TRANSMIT, &notify_ready,
507                                                NULL);
508 }
509
510 static void
511 testing_connect_cb (struct PeerContext *p1, struct PeerContext *p2, void *cls)
512 {
513   char *p1_c = GNUNET_strdup (GNUNET_i2s (&p1->id));
514
515   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peers connected: %s <-> %s\n", p1_c,
516               GNUNET_i2s (&p2->id));
517   GNUNET_free (p1_c);
518
519   test_connected = GNUNET_YES;
520   cc = NULL;
521
522   GNUNET_SCHEDULER_add_now (&sendtask, NULL);
523 }
524
525 static void
526 start_cb (struct PeerContext *p, void *cls)
527 {
528   static int started;
529   started++;
530
531   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer %u (`%s') started\n", p->no,
532               GNUNET_i2s (&p->id));
533
534   if (started != 2)
535     return;
536
537   test_connected = GNUNET_NO;
538   cc = GNUNET_TRANSPORT_TESTING_connect_peers (tth, p1, p2, &testing_connect_cb,
539                                                NULL);
540
541 }
542
543 static void
544 run (void *cls, char *const *args, const char *cfgfile,
545      const struct GNUNET_CONFIGURATION_Handle *cfg)
546 {
547   die_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
548   test_send_timeout = GNUNET_NO;
549
550   p1 = GNUNET_TRANSPORT_TESTING_start_peer (tth, cfg_file_p1, 1,
551                                             &notify_receive, &notify_connect,
552                                             &notify_disconnect, &start_cb,
553                                             NULL);
554   p2 = GNUNET_TRANSPORT_TESTING_start_peer (tth, cfg_file_p2, 2,
555                                             &notify_receive, &notify_connect,
556                                             &notify_disconnect, &start_cb,
557                                             NULL);
558   if ((p1 == NULL) || (p2 == NULL))
559   {
560     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Fail! Could not start peers!\n");
561     if (die_task != NULL)
562       GNUNET_SCHEDULER_cancel (die_task);
563     //die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
564     return;
565   }
566 }
567
568
569 int
570 main (int argc, char *argv[])
571 {
572   char *test_source;
573   int ret;
574
575   static char *const argv_new[] = { "test-transport-api-reliability",
576     "-c",
577     "test_transport_api_data.conf",
578     NULL
579   };
580   static struct GNUNET_GETOPT_CommandLineOption options[] = {
581     GNUNET_GETOPT_OPTION_END
582   };
583
584   GNUNET_TRANSPORT_TESTING_get_test_name (argv[0], &test_name);
585
586   GNUNET_log_setup (test_name,
587                     "WARNING",
588                     NULL);
589
590   GNUNET_TRANSPORT_TESTING_get_test_source_name (__FILE__, &test_source);
591   GNUNET_TRANSPORT_TESTING_get_test_plugin_name (argv[0], test_source,
592                                                  &test_plugin);
593
594   tth = GNUNET_TRANSPORT_TESTING_init ();
595
596   GNUNET_TRANSPORT_TESTING_get_config_name (argv[0], &cfg_file_p1, 1);
597   GNUNET_TRANSPORT_TESTING_get_config_name (argv[0], &cfg_file_p2, 2);
598
599
600
601 #if WRITECONFIG
602   setTransportOptions ("test_transport_api_data.conf");
603 #endif
604   ok = GNUNET_SYSERR;
605
606   ret = GNUNET_PROGRAM_run ((sizeof (argv_new) / sizeof (char *)) - 1, argv_new, test_name,
607                       "nohelp", options, &run, &ok);
608   if (GNUNET_SYSERR == ret)
609     ok = -1;
610
611   GNUNET_free (cfg_file_p1);
612   GNUNET_free (cfg_file_p2);
613
614   GNUNET_free (test_source);
615   GNUNET_free (test_plugin);
616   GNUNET_free (test_name);
617
618   return ok;
619 }
620
621 /* end of test_transport_api_reliability.c */