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