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