e1ce4da18ddd893d5873db3b4b1f035c7b96c56d
[oweals/gnunet.git] / src / transport / test_transport_api_reliability.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009, 2010, 2016 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, 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  * Allow making the problem "bigger".
36  */
37 #define FACTOR 1
38
39 /**
40  * Total number of messages to send
41  *
42  * Note that this value must not significantly exceed
43  * 'MAX_PENDING' in 'gnunet-service-transport_clients.c', otherwise
44  * messages may be dropped even for a reliable transport.
45  */
46 #define TOTAL_MSGS (1024 * 3 * FACTOR)
47
48 /**
49  * Message type of test messages
50  */
51 #define MTYPE 12345
52
53 /**
54  * Testcase timeout
55  */
56 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 90 * FACTOR)
57
58 /**
59  * How long until we give up on transmitting the message?
60  */
61 #define TIMEOUT_TRANSMIT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60 * FACTOR)
62
63
64 GNUNET_NETWORK_STRUCT_BEGIN
65
66 /**
67  * Struct for the test message
68  */
69 struct TestMessage
70 {
71   struct GNUNET_MessageHeader header;
72   uint32_t num GNUNET_PACKED;
73 };
74 GNUNET_NETWORK_STRUCT_END
75
76
77 static struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc;
78
79 static struct GNUNET_TRANSPORT_TransmitHandle *th;
80
81 /**
82  * Total amount of bytes sent
83  */
84 static unsigned long long total_bytes;
85
86 /**
87  * Time of start
88  */
89 static struct GNUNET_TIME_Absolute start_time;
90
91 /**
92  * No. of message currently scheduled to be send
93  */
94 static int msg_scheduled;
95
96 /**
97  * No. of last message sent
98  */
99 static int msg_sent;
100
101 /**
102  * No. of last message received
103  */
104 static int msg_recv;
105
106 /**
107  * Bitmap storing which messages were received
108  */
109 static char bitmap[TOTAL_MSGS / 8];
110
111
112 static unsigned int
113 get_size (unsigned int iter)
114 {
115   unsigned int ret;
116
117   ret = (iter * iter * iter);
118
119 #ifndef LINUX
120   /* FreeBSD/OSX etc. Unix DGRAMs do not work
121    * with large messages */
122   if (0 == strcmp ("unix", test_plugin))
123     return sizeof (struct TestMessage) + (ret % 1024);
124 #endif
125   return sizeof (struct TestMessage) + (ret % 60000);
126 }
127
128
129 /**
130  * Sets a bit active in the bitmap.
131  *
132  * @param bitIdx which bit to set
133  * @return #GNUNET_SYSERR on error, #GNUNET_OK on success
134  */
135 static int
136 set_bit (unsigned int bitIdx)
137 {
138   size_t arraySlot;
139   unsigned int targetBit;
140
141   if (bitIdx >= sizeof (bitmap) * 8)
142   {
143     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
144                 "tried to set bit %u of %u(!?!?)\n",
145                 bitIdx,
146                 (unsigned int) sizeof (bitmap) * 8);
147     return GNUNET_SYSERR;
148   }
149   arraySlot = bitIdx / 8;
150   targetBit = (1L << (bitIdx % 8));
151   bitmap[arraySlot] |= targetBit;
152   return GNUNET_OK;
153 }
154
155
156 /**
157  * Obtain a bit from bitmap.
158  * @param map the bitmap
159  * @param bit index from bitmap
160  *
161  * @return Bit @a bit from @a map
162  */
163 static int
164 get_bit (const char *map,
165          unsigned int bit)
166 {
167   if (bit > TOTAL_MSGS)
168   {
169     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
170                 "get bit %u of %u(!?!?)\n",
171                 bit,
172                 (unsigned int) sizeof (bitmap) * 8);
173     return 0;
174   }
175   return ((map)[bit >> 3] & (1 << (bit & 7))) > 0;
176 }
177
178
179 static void
180 custom_shutdown (void *cls)
181 {
182   unsigned long long delta;
183   unsigned long long rate;
184   unsigned int i;
185   int ok;
186
187   if (NULL != th)
188   {
189     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
190     th = NULL;
191   }
192   /* Calculcate statistics   */
193   delta = GNUNET_TIME_absolute_get_duration (start_time).rel_value_us;
194   rate = (1000LL* 1000ll * total_bytes) / (1024 * delta);
195   FPRINTF (stderr,
196            "\nThroughput was %llu KiBytes/s\n",
197            rate);
198   {
199     char *value_name;
200     GNUNET_asprintf (&value_name,
201                      "unreliable_%s",
202                      ccc->test_plugin);
203     GAUGER ("TRANSPORT",
204             value_name,
205             (int) rate,
206             "kb/s");
207     GNUNET_free (value_name);
208   }
209
210   ok = 0;
211   for (i = 0; i < TOTAL_MSGS; i++)
212   {
213     if (get_bit (bitmap, i) == 0)
214     {
215       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
216                   "Did not receive message %d\n",
217                   i);
218       ok = -1;
219     }
220   }
221   if (0 != ok)
222     ccc->global_ret = GNUNET_SYSERR; /* fail: messages missing! */
223 }
224
225
226 static void
227 notify_receive (void *cls,
228                 struct GNUNET_TRANSPORT_TESTING_PeerContext *receiver,
229                 const struct GNUNET_PeerIdentity *sender,
230                 const struct GNUNET_MessageHeader *message)
231 {
232   static int n;
233
234   unsigned int s;
235   char cbuf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
236   const struct TestMessage *hdr;
237
238   hdr = (const struct TestMessage *) message;
239
240   if (MTYPE != ntohs (message->type))
241     return;
242   msg_recv = ntohl (hdr->num);
243   s = get_size (ntohl (hdr->num));
244
245   if (ntohs (message->size) != s)
246   {
247     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
248                 "Expected message %u of size %u, got %u bytes of message %u\n",
249                 ntohl (hdr->num),
250                 s,
251                 ntohs (message->size),
252                 ntohl (hdr->num));
253     ccc->global_ret = GNUNET_SYSERR;
254     GNUNET_SCHEDULER_shutdown ();
255     return;
256   }
257
258   memset (cbuf, ntohl (hdr->num), s - sizeof (struct TestMessage));
259   if (0 != memcmp (cbuf, &hdr[1], s - sizeof (struct TestMessage)))
260   {
261     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
262                 "Expected message %u with bits %u, but body did not match\n",
263                 ntohl (hdr->num),
264                 (unsigned char) n);
265     ccc->global_ret = GNUNET_SYSERR;
266     GNUNET_SCHEDULER_shutdown ();
267     return;
268   }
269 #if VERBOSE
270   if (ntohl (hdr->num) % 5 == 0)
271   {
272     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
273                 "Got message %u of size %u\n",
274                 ntohl (hdr->num),
275                 ntohs (message->size));
276   }
277 #endif
278   n++;
279   if (GNUNET_SYSERR == set_bit (ntohl (hdr->num)))
280   {
281       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
282                   "Message id %u is bigger than maxmimum number of messages %u expected\n",
283                   ntohl (hdr->num),
284                   TOTAL_MSGS);
285   }
286   if (0 == (n % (TOTAL_MSGS / 100)))
287   {
288     FPRINTF (stderr, "%s",  ".");
289   }
290   if (n == TOTAL_MSGS)
291   {
292     /* end testcase with success */
293     GNUNET_SCHEDULER_shutdown ();
294   }
295 }
296
297
298 static size_t
299 notify_ready (void *cls,
300               size_t size,
301               void *buf)
302 {
303   static int n;
304   char *cbuf = buf;
305   struct TestMessage hdr;
306   unsigned int s;
307   unsigned int ret;
308
309   th = NULL;
310   if (NULL == buf)
311   {
312     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
313                 "Timeout occurred while waiting for transmit_ready for msg %u of %u\n",
314                 msg_scheduled,
315                 TOTAL_MSGS);
316     GNUNET_SCHEDULER_shutdown ();
317     ccc->global_ret = 42;
318     return 0;
319   }
320   ret = 0;
321   s = get_size (n);
322   GNUNET_assert (size >= s);
323   GNUNET_assert (buf != NULL);
324   GNUNET_assert (n < TOTAL_MSGS);
325   cbuf = buf;
326   do
327   {
328     GNUNET_assert (n < TOTAL_MSGS);
329     hdr.header.size = htons (s);
330     hdr.header.type = htons (MTYPE);
331     hdr.num = htonl (n);
332     msg_sent = n;
333     GNUNET_memcpy (&cbuf[ret], &hdr, sizeof (struct TestMessage));
334     ret += sizeof (struct TestMessage);
335     memset (&cbuf[ret], n, s - sizeof (struct TestMessage));
336     ret += s - sizeof (struct TestMessage);
337
338 #if VERBOSE
339     if (0 == n % 5000)
340     {
341       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
342                   "Sending message %u of size %u\n",
343                   n,
344                   s);
345     }
346 #endif
347     n++;
348     s = get_size (n);
349     if (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 16))
350       break;                    /* sometimes pack buffer full, sometimes not */
351   }
352   while ((size - ret >= s) && (n < TOTAL_MSGS));
353   if (n < TOTAL_MSGS)
354   {
355     th = GNUNET_TRANSPORT_notify_transmit_ready (ccc->p[1]->th,
356                                                  &ccc->p[0]->id,
357                                                  s,
358                                                  TIMEOUT_TRANSMIT,
359                                                  &notify_ready,
360                                                  NULL);
361     msg_scheduled = n;
362   }
363   else
364   {
365     FPRINTF (stderr,
366              "%s",
367              "\n");
368     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
369                 "All messages scheduled to be sent\n");
370   }
371   if (0 == n % 5000)
372   {
373     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
374                 "Returning total message block of size %u\n",
375                 ret);
376   }
377   total_bytes += ret;
378   return ret;
379 }
380
381
382 static void
383 sendtask (void *cls)
384 {
385   start_time = GNUNET_TIME_absolute_get ();
386   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
387               "Starting to send %u messages\n",
388               TOTAL_MSGS);
389   th = GNUNET_TRANSPORT_notify_transmit_ready (ccc->p[1]->th,
390                                                &ccc->p[0]->id,
391                                                get_size (0),
392                                                TIMEOUT_TRANSMIT,
393                                                &notify_ready,
394                                                NULL);
395 }
396
397
398 static void
399 notify_disconnect (void *cls,
400                    struct GNUNET_TRANSPORT_TESTING_PeerContext *me,
401                    const struct GNUNET_PeerIdentity *other)
402 {
403   GNUNET_TRANSPORT_TESTING_log_disconnect (cls,
404                                            me,
405                                            other);
406   if (NULL != th)
407   {
408     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
409     th = NULL;
410   }
411 }
412
413
414 int
415 main (int argc, char *argv[])
416 {
417   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext my_ccc = {
418     .connect_continuation = &sendtask,
419     .config_file = "test_transport_api_data.conf",
420     .rec = &notify_receive,
421     .nc = &GNUNET_TRANSPORT_TESTING_log_connect,
422     .nd = &notify_disconnect,
423     .shutdown_task = &custom_shutdown,
424     .timeout = TIMEOUT
425   };
426
427   ccc = &my_ccc;
428   if (GNUNET_OK !=
429       GNUNET_TRANSPORT_TESTING_main (2,
430                                      &GNUNET_TRANSPORT_TESTING_connect_check,
431                                      ccc))
432     return 1;
433   return 0;
434 }
435
436
437 /* end of test_transport_api_reliability.c */