add -w option to gnunet-config
[oweals/gnunet.git] / src / cadet / test_cadet.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2011 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 cadet/test_cadet.c
22  *
23  * @brief Test for the cadet service: retransmission of traffic.
24  */
25 #include <stdio.h>
26 #include "platform.h"
27 #include "cadet_test_lib.h"
28 #include "gnunet_cadet_service.h"
29 #include "gnunet_statistics_service.h"
30 #include <gauger.h>
31
32
33 /**
34  * How many messages to send
35  */
36 #define TOTAL_PACKETS 200 /* Cannot exceed 64k! */
37
38 /**
39  * How long until we give up on connecting the peers?
40  */
41 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120)
42
43 /**
44  * Time to wait for stuff that should be rather fast
45  */
46 #define SHORT_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 20)
47
48 /**
49  * DIFFERENT TESTS TO RUN
50  */
51 #define SETUP 0
52 #define FORWARD 1
53 #define KEEPALIVE 2
54 #define SPEED 3
55 #define SPEED_ACK 4
56 #define SPEED_REL 8
57 #define P2P_SIGNAL 10
58
59 /**
60  * Which test are we running?
61  */
62 static int test;
63
64 /**
65  * String with test name
66  */
67 char *test_name;
68
69 /**
70  * Flag to send traffic leaf->root in speed tests to test BCK_ACK logic.
71  */
72 static int test_backwards = GNUNET_NO;
73
74 /**
75  * How many events have happened
76  */
77 static int ok;
78
79 /**
80  * Number of events expected to conclude the test successfully.
81  */
82 int ok_goal;
83
84 /**
85  * Size of each test packet
86  */
87 size_t size_payload = sizeof (struct GNUNET_MessageHeader) + sizeof (uint32_t);
88
89 /**
90  * Operation to get peer ids.
91  */
92 struct GNUNET_TESTBED_Operation *t_op[2];
93
94 /**
95  * Peer ids.
96  */
97 struct GNUNET_PeerIdentity *p_id[2];
98
99 /**
100  * Peer ids counter.
101  */
102 unsigned int p_ids;
103
104 /**
105  * Is the setup initialized?
106  */
107 static int initialized;
108
109 /**
110  * Number of payload packes sent.
111  */
112 static int data_sent;
113
114 /**
115  * Number of payload packets received.
116  */
117 static int data_received;
118
119 /**
120  * Number of payload packed acknowledgements sent.
121  */
122 static int ack_sent;
123
124 /**
125  * Number of payload packed explicitly (app level) acknowledged.
126  */
127 static int ack_received;
128
129 /**
130  * Total number of peers asked to run.
131  */
132 static unsigned long long peers_requested;
133
134 /**
135  * Number of currently running peers (should be same as @c peers_requested).
136  */
137 static unsigned long long peers_running;
138
139 /**
140  * Test context (to shut down).
141  */
142 struct GNUNET_CADET_TEST_Context *test_ctx;
143
144 /**
145  * Task called to disconnect peers.
146  */
147 static struct GNUNET_SCHEDULER_Task *disconnect_task;
148
149 /**
150  * Task To perform tests
151  */
152 static struct GNUNET_SCHEDULER_Task *test_task;
153
154 /**
155  * Task runnining #data_task().
156  */
157 static struct GNUNET_SCHEDULER_Task *data_job;
158
159 /**
160  * Cadet handle for the root peer
161  */
162 static struct GNUNET_CADET_Handle *h1;
163
164 /**
165  * Cadet handle for the first leaf peer
166  */
167 static struct GNUNET_CADET_Handle *h2;
168
169 /**
170  * Channel handle for the root peer
171  */
172 static struct GNUNET_CADET_Channel *ch;
173
174 /**
175  * Channel handle for the dest peer
176  */
177 static struct GNUNET_CADET_Channel *incoming_ch;
178
179 /**
180  * Transmit handle for root data calls
181  */
182 static struct GNUNET_CADET_TransmitHandle *th;
183
184 /**
185  * Transmit handle for root data calls
186  */
187 static struct GNUNET_CADET_TransmitHandle *incoming_th;
188
189
190 /**
191  * Time we started the data transmission (after channel has been established
192  * and initilized).
193  */
194 static struct GNUNET_TIME_Absolute start_time;
195
196 /**
197  * Peers handle.
198  */
199 static struct GNUNET_TESTBED_Peer **testbed_peers;
200
201 /**
202  * Statistics operation handle.
203  */
204 static struct GNUNET_TESTBED_Operation *stats_op;
205
206 /**
207  * Keepalives sent.
208  */
209 static unsigned int ka_sent;
210
211 /**
212  * Keepalives received.
213  */
214 static unsigned int ka_received;
215
216
217 /**
218  * Get the client number considered as the "target" or "receiver", depending on
219  * the test type and size.
220  *
221  * @return Peer # of the target client, either 0 (for backward tests) or
222  *         the last peer in the line (for other tests).
223  */
224 static unsigned int
225 get_expected_target ()
226 {
227   if (SPEED == test && GNUNET_YES == test_backwards)
228     return 0;
229   else
230     return peers_requested - 1;
231 }
232
233
234 /**
235  * Show the results of the test (banwidth acheived) and log them to GAUGER
236  */
237 static void
238 show_end_data (void)
239 {
240   static struct GNUNET_TIME_Absolute end_time;
241   static struct GNUNET_TIME_Relative total_time;
242
243   end_time = GNUNET_TIME_absolute_get();
244   total_time = GNUNET_TIME_absolute_get_difference(start_time, end_time);
245   FPRINTF (stderr, "\nResults of test \"%s\"\n", test_name);
246   FPRINTF (stderr, "Test time %s\n",
247            GNUNET_STRINGS_relative_time_to_string (total_time,
248                                                    GNUNET_YES));
249   FPRINTF (stderr, "Test bandwidth: %f kb/s\n",
250            4 * TOTAL_PACKETS * 1.0 / (total_time.rel_value_us / 1000)); // 4bytes * ms
251   FPRINTF (stderr, "Test throughput: %f packets/s\n\n",
252            TOTAL_PACKETS * 1000.0 / (total_time.rel_value_us / 1000)); // packets * ms
253   GAUGER ("CADET", test_name,
254           TOTAL_PACKETS * 1000.0 / (total_time.rel_value_us / 1000),
255           "packets/s");
256 }
257
258
259 /**
260  * Disconnect from cadet services af all peers, call shutdown.
261  *
262  * @param cls Closure (line number from which termination was requested).
263  * @param tc Task Context.
264  */
265 static void
266 disconnect_cadet_peers (void *cls)
267 {
268   long line = (long) cls;
269   unsigned int i;
270
271   disconnect_task = NULL;
272   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
273               "disconnecting cadet service of peers, called from line %ld\n",
274               line);
275   for (i = 0; i < 2; i++)
276   {
277     GNUNET_TESTBED_operation_done (t_op[i]);
278   }
279   if (NULL != ch)
280   {
281     if (NULL != th)
282     {
283       GNUNET_CADET_notify_transmit_ready_cancel (th);
284       th = NULL;
285     }
286     GNUNET_CADET_channel_destroy (ch);
287     ch = NULL;
288   }
289   if (NULL != incoming_ch)
290   {
291     if (NULL != incoming_th)
292     {
293       GNUNET_CADET_notify_transmit_ready_cancel (incoming_th);
294       incoming_th = NULL;
295     }
296     GNUNET_CADET_channel_destroy (incoming_ch);
297     incoming_ch = NULL;
298   }
299   GNUNET_CADET_TEST_cleanup (test_ctx);
300   GNUNET_SCHEDULER_shutdown ();
301 }
302
303
304 /**
305  * Shut down peergroup, clean up.
306  *
307  * @param cls Closure (unused).
308  * @param tc Task Context.
309  */
310 static void
311 shutdown_task (void *cls)
312 {
313   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ending test.\n");
314   if (NULL != data_job)
315   {
316     GNUNET_SCHEDULER_cancel (data_job);
317     data_job = NULL;
318   }
319   if (NULL != test_task)
320   {
321     GNUNET_SCHEDULER_cancel (test_task);
322     test_task = NULL;
323   }
324   if (NULL != disconnect_task)
325   {
326     GNUNET_SCHEDULER_cancel (disconnect_task);
327     disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_cadet_peers,
328                                                 (void *) __LINE__);
329   }
330 }
331
332
333 /**
334  * Stats callback. Finish the stats testbed operation and when all stats have
335  * been iterated, shutdown the test.
336  *
337  * @param cls Closure (line number from which termination was requested).
338  * @param op the operation that has been finished
339  * @param emsg error message in case the operation has failed; will be NULL if
340  *          operation has executed successfully.
341  */
342 static void
343 stats_cont (void *cls, struct GNUNET_TESTBED_Operation *op, const char *emsg)
344 {
345   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " KA sent: %u, KA received: %u\n",
346               ka_sent, ka_received);
347   if (KEEPALIVE == test && (ka_sent < 2 || ka_sent > ka_received + 1))
348     ok--;
349   GNUNET_TESTBED_operation_done (stats_op);
350
351   if (NULL != disconnect_task)
352     GNUNET_SCHEDULER_cancel (disconnect_task);
353   disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_cadet_peers,
354                                               cls);
355 }
356
357
358 /**
359  * Process statistic values.
360  *
361  * @param cls closure (line number, unused)
362  * @param peer the peer the statistic belong to
363  * @param subsystem name of subsystem that created the statistic
364  * @param name the name of the datum
365  * @param value the current value
366  * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
367  * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
368  */
369 static int
370 stats_iterator (void *cls, const struct GNUNET_TESTBED_Peer *peer,
371                 const char *subsystem, const char *name,
372                 uint64_t value, int is_persistent)
373 {
374   static const char *s_sent = "# keepalives sent";
375   static const char *s_recv = "# keepalives received";
376   uint32_t i;
377
378   i = GNUNET_TESTBED_get_index (peer);
379   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "STATS PEER %u - %s [%s]: %llu\n",
380               i, subsystem, name, value);
381   if (0 == strncmp (s_sent, name, strlen (s_sent)) && 0 == i)
382     ka_sent = value;
383
384   if (0 == strncmp(s_recv, name, strlen (s_recv)) && peers_requested - 1 == i)
385     ka_received = value;
386
387   return GNUNET_OK;
388 }
389
390
391 /**
392  * Task to gather all statistics.
393  *
394  * @param cls Closure (NULL).
395  */
396 static void
397 gather_stats_and_exit (void *cls)
398 {
399   long l = (long) cls;
400
401   disconnect_task = NULL;
402   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
403               "gathering statistics from line %d\n",
404               l);
405   if (NULL != ch)
406   {
407     if (NULL != th)
408     {
409       GNUNET_CADET_notify_transmit_ready_cancel (th);
410       th = NULL;
411     }
412     GNUNET_CADET_channel_destroy (ch);
413     ch = NULL;
414   }
415   stats_op = GNUNET_TESTBED_get_statistics (peers_running, testbed_peers,
416                                             "cadet", NULL,
417                                             stats_iterator, stats_cont, cls);
418 }
419
420
421
422 /**
423  * Abort test: schedule disconnect and shutdown immediately
424  *
425  * @param line Line in the code the abort is requested from (__LINE__).
426  */
427 static void
428 abort_test (long line)
429 {
430   if (disconnect_task != NULL)
431   {
432     GNUNET_SCHEDULER_cancel (disconnect_task);
433     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Aborting test from %ld\n", line);
434     disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_cadet_peers,
435                                                 (void *) line);
436   }
437 }
438
439 /**
440  * Transmit ready callback.
441  *
442  * @param cls Closure (message type).
443  * @param size Size of the tranmist buffer.
444  * @param buf Pointer to the beginning of the buffer.
445  *
446  * @return Number of bytes written to buf.
447  */
448 static size_t
449 tmt_rdy (void *cls, size_t size, void *buf);
450
451
452 /**
453  * Task to request a new data transmission.
454  *
455  * @param cls Closure (peer #).
456  */
457 static void
458 data_task (void *cls)
459 {
460   struct GNUNET_CADET_Channel *channel;
461   static struct GNUNET_CADET_TransmitHandle **pth;
462   long src;
463
464   data_job = NULL;
465   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Data task\n");
466   if (GNUNET_YES == test_backwards)
467   {
468     channel = incoming_ch;
469     pth = &incoming_th;
470     src = peers_requested - 1;
471   }
472   else
473   {
474     channel = ch;
475     pth = &th;
476     src = 0;
477   }
478
479   GNUNET_assert (NULL != channel);
480   GNUNET_assert (NULL == *pth);
481
482   *pth = GNUNET_CADET_notify_transmit_ready (channel, GNUNET_NO,
483                                              GNUNET_TIME_UNIT_FOREVER_REL,
484                                              size_payload + data_sent,
485                                              &tmt_rdy, (void *) src);
486   if (NULL == *pth)
487   {
488     unsigned long i = (unsigned long) cls;
489
490     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Retransmission\n");
491     if (0 == i)
492     {
493       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "  in 1 ms\n");
494       data_job = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
495                                                &data_task, (void *) 1L);
496     }
497     else
498     {
499       i++;
500       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "in %u ms\n", i);
501       data_job = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
502                                                                               i),
503                                                &data_task, (void *) i);
504     }
505   }
506 }
507
508
509 /**
510  * Transmit ready callback
511  *
512  * @param cls Closure (peer # which is sending the data).
513  * @param size Size of the buffer we have.
514  * @param buf Buffer to copy data to.
515  */
516 size_t
517 tmt_rdy (void *cls, size_t size, void *buf)
518 {
519   struct GNUNET_MessageHeader *msg = buf;
520   size_t msg_size;
521   uint32_t *data;
522   long id = (long) cls;
523   unsigned int counter;
524
525   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tmt_rdy on %ld, filling buffer\n", id);
526   if (0 == id)
527     th = NULL;
528   else if ((peers_requested - 1) == id)
529     incoming_th = NULL;
530   else
531     GNUNET_assert (0);
532   counter = get_expected_target () == id ? ack_sent : data_sent;
533   msg_size = size_payload + counter;
534   if (size < msg_size || NULL == buf)
535   {
536     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
537                 "size %u, buf %p, data_sent %u, ack_received %u\n",
538                 size, buf, data_sent, ack_received);
539     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ok %u, ok goal %u\n", ok, ok_goal);
540     GNUNET_break (ok >= ok_goal - 2);
541
542     return 0;
543   }
544   msg->size = htons (msg_size);
545   msg->type = htons (1);
546   data = (uint32_t *) &msg[1];
547   *data = htonl (counter);
548   if (GNUNET_NO == initialized)
549   {
550     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "sending initializer\n");
551     msg_size = size_payload + 1000;
552     if (SPEED_ACK == test)
553       data_sent++;
554   }
555   else if (SPEED == test || SPEED_ACK == test)
556   {
557     if (get_expected_target() == id)
558       ack_sent++;
559     else
560       data_sent++;
561     counter++;
562     GNUNET_log (GNUNET_ERROR_TYPE_INFO, " Sent message %d size %u\n",
563                 counter, msg_size);
564     if (data_sent < TOTAL_PACKETS && SPEED == test)
565     {
566       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " Scheduling message %d\n",
567                   counter + 1);
568       data_job = GNUNET_SCHEDULER_add_now (&data_task, NULL);
569     }
570   }
571
572   return msg_size;
573 }
574
575
576 /**
577  * Function is called whenever a message is received.
578  *
579  * @param cls closure (set from GNUNET_CADET_connect, peer number)
580  * @param channel connection to the other end
581  * @param channel_ctx place to store local state associated with the channel
582  * @param message the actual message
583  * @return GNUNET_OK to keep the connection open,
584  *         GNUNET_SYSERR to close it (signal serious error)
585  */
586 int
587 data_callback (void *cls, struct GNUNET_CADET_Channel *channel,
588                void **channel_ctx,
589                const struct GNUNET_MessageHeader *message)
590 {
591   struct GNUNET_CADET_TransmitHandle **pth;
592   long client = (long) cls;
593   long expected_target_client;
594   uint32_t *data;
595   uint32_t payload;
596   unsigned int counter;
597
598   ok++;
599   counter = get_expected_target () == client ? data_received : ack_received;
600
601   GNUNET_CADET_receive_done (channel);
602
603   if ((ok % 10) == 0)
604   {
605     if (NULL != disconnect_task)
606     {
607       GNUNET_log (GNUNET_ERROR_TYPE_INFO, " reschedule timeout\n");
608       GNUNET_SCHEDULER_cancel (disconnect_task);
609       disconnect_task = GNUNET_SCHEDULER_add_delayed (SHORT_TIME,
610                                                       &gather_stats_and_exit,
611                                                       (void *) __LINE__);
612     }
613   }
614
615   switch (client)
616   {
617   case 0L:
618     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Root client got a message!\n");
619     GNUNET_assert (channel == ch);
620     pth = &th;
621     break;
622   case 1L:
623   case 4L:
624     GNUNET_assert (client == peers_requested - 1);
625     GNUNET_assert (channel == incoming_ch);
626     pth = &incoming_th;
627     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Leaf client %ld got a message.\n",
628                 client);
629     break;
630   default:
631     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Client %ld not valid.\n", client);
632     GNUNET_assert (0);
633   }
634   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: (%d/%d)\n", ok, ok_goal);
635   data = (uint32_t *) &message[1];
636   payload = ntohl (*data);
637   if (payload == counter)
638   {
639     GNUNET_log (GNUNET_ERROR_TYPE_INFO, " payload as expected: %u\n", payload);
640   }
641   else
642   {
643     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, " payload %u, expected: %u\n",
644                 payload, counter);
645   }
646   expected_target_client = get_expected_target ();
647
648   if (GNUNET_NO == initialized)
649   {
650     initialized = GNUNET_YES;
651     start_time = GNUNET_TIME_absolute_get ();
652     if (SPEED == test)
653     {
654       GNUNET_assert (peers_requested - 1 == client);
655       data_job = GNUNET_SCHEDULER_add_now (&data_task, NULL);
656       return GNUNET_OK;
657     }
658   }
659
660   counter++;
661   if (client == expected_target_client) /* Normally 4 */
662   {
663     data_received++;
664     GNUNET_log (GNUNET_ERROR_TYPE_INFO, " received data %u\n", data_received);
665     if (SPEED != test || (ok_goal - 2) == ok)
666     {
667       /* Send ACK */
668       GNUNET_assert (NULL == *pth);
669       *pth = GNUNET_CADET_notify_transmit_ready (channel, GNUNET_NO,
670                                                  GNUNET_TIME_UNIT_FOREVER_REL,
671                                                  size_payload + ack_sent,
672                                                  &tmt_rdy, (void *) client);
673       return GNUNET_OK;
674     }
675     else
676     {
677       if (data_received < TOTAL_PACKETS)
678         return GNUNET_OK;
679     }
680   }
681   else /* Normally 0 */
682   {
683     if (SPEED_ACK == test || SPEED == test)
684     {
685       ack_received++;
686       GNUNET_log (GNUNET_ERROR_TYPE_INFO, " received ack %u\n", ack_received);
687       /* send more data */
688       GNUNET_assert (NULL == *pth);
689       *pth = GNUNET_CADET_notify_transmit_ready (channel, GNUNET_NO,
690                                                  GNUNET_TIME_UNIT_FOREVER_REL,
691                                                  size_payload + data_sent,
692                                                  &tmt_rdy, (void *) client);
693       if (ack_received < TOTAL_PACKETS && SPEED != test)
694         return GNUNET_OK;
695       if (ok == 2 && SPEED == test)
696         return GNUNET_OK;
697       show_end_data();
698     }
699     if (test == P2P_SIGNAL)
700     {
701       if (NULL != incoming_th)
702       {
703         GNUNET_CADET_notify_transmit_ready_cancel (incoming_th);
704         incoming_th = NULL;
705       }
706       GNUNET_CADET_channel_destroy (incoming_ch);
707       incoming_ch = NULL;
708     }
709     else
710     {
711       if (NULL != th)
712       {
713         GNUNET_CADET_notify_transmit_ready_cancel (th);
714         th = NULL;
715       }
716       GNUNET_CADET_channel_destroy (ch);
717       ch = NULL;
718     }
719   }
720
721   return GNUNET_OK;
722 }
723
724
725 /**
726  * Data handlers for every message type of CADET's payload.
727  * {callback_function, message_type, size_expected}
728  */
729 static struct GNUNET_CADET_MessageHandler handlers[] = {
730   {&data_callback, 1, sizeof (struct GNUNET_MessageHeader)},
731   {NULL, 0, 0}
732 };
733
734
735 /**
736  * Method called whenever another peer has added us to a channel
737  * the other peer initiated.
738  *
739  * @param cls Closure.
740  * @param channel New handle to the channel.
741  * @param initiator Peer that started the channel.
742  * @param port Port this channel is connected to.
743  * @param options channel option flags
744  * @return Initial channel context for the channel
745  *         (can be NULL -- that's not an error).
746  */
747 static void *
748 incoming_channel (void *cls, struct GNUNET_CADET_Channel *channel,
749                  const struct GNUNET_PeerIdentity *initiator,
750                  uint32_t port, enum GNUNET_CADET_ChannelOption options)
751 {
752   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
753               "Incoming channel from %s to peer %d\n",
754               GNUNET_i2s (initiator), (long) cls);
755   ok++;
756   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
757   if ((long) cls == peers_requested - 1)
758     incoming_ch = channel;
759   else
760   {
761     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
762                 "Incoming channel for unknown client %lu\n", (long) cls);
763     GNUNET_break(0);
764   }
765   if (NULL != disconnect_task)
766   {
767     GNUNET_SCHEDULER_cancel (disconnect_task);
768     disconnect_task = GNUNET_SCHEDULER_add_delayed (SHORT_TIME,
769                                                     &gather_stats_and_exit,
770                                                     (void *) __LINE__);
771   }
772
773   return NULL;
774 }
775
776 /**
777  * Function called whenever an inbound channel is destroyed.  Should clean up
778  * any associated state.
779  *
780  * @param cls closure (set from GNUNET_CADET_connect, peer number)
781  * @param channel connection to the other end (henceforth invalid)
782  * @param channel_ctx place where local state associated
783  *                   with the channel is stored
784  */
785 static void
786 channel_cleaner (void *cls, const struct GNUNET_CADET_Channel *channel,
787                  void *channel_ctx)
788 {
789   long i = (long) cls;
790
791   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
792               "Incoming channel disconnected at peer %ld\n", i);
793   if (peers_running - 1 == i)
794   {
795     ok++;
796     GNUNET_break (channel == incoming_ch);
797     incoming_ch = NULL;
798   }
799   else if (0L == i)
800   {
801     if (P2P_SIGNAL == test)
802     {
803       ok ++;
804     }
805     GNUNET_break (channel == ch);
806     ch = NULL;
807   }
808   else
809     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
810                 "Unknown peer! %d\n", i);
811   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
812
813   if (NULL != disconnect_task)
814   {
815     GNUNET_SCHEDULER_cancel (disconnect_task);
816     disconnect_task = GNUNET_SCHEDULER_add_now (&gather_stats_and_exit,
817                                                 (void *) __LINE__);
818   }
819
820   return;
821 }
822
823
824 /**
825  * START THE TESTCASE ITSELF, AS WE ARE CONNECTED TO THE CADET SERVICES.
826  *
827  * Testcase continues when the root receives confirmation of connected peers,
828  * on callback function ch.
829  *
830  * @param cls Closure (unused).
831  */
832 static void
833 do_test (void *cls)
834 {
835   enum GNUNET_CADET_ChannelOption flags;
836
837   test_task = NULL;
838   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "do_test\n");
839
840   if (NULL != disconnect_task)
841   {
842     GNUNET_SCHEDULER_cancel (disconnect_task);
843     disconnect_task = NULL;
844   }
845
846   flags = GNUNET_CADET_OPTION_DEFAULT;
847   if (SPEED_REL == test)
848   {
849     test = SPEED;
850     flags |= GNUNET_CADET_OPTION_RELIABLE;
851   }
852   ch = GNUNET_CADET_channel_create (h1, NULL, p_id[1], 1, flags);
853
854   disconnect_task = GNUNET_SCHEDULER_add_delayed (SHORT_TIME,
855                                                   &gather_stats_and_exit,
856                                                   (void *) __LINE__);
857   if (KEEPALIVE == test)
858     return; /* Don't send any data. */
859
860   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending data initializer...\n");
861   data_received = 0;
862   data_sent = 0;
863   ack_received = 0;
864   ack_sent = 0;
865   th = GNUNET_CADET_notify_transmit_ready (ch, GNUNET_NO,
866                                            GNUNET_TIME_UNIT_FOREVER_REL,
867                                            size_payload + 1000,
868                                            &tmt_rdy, (void *) 0L);
869 }
870
871
872 /**
873  * Callback to be called when the requested peer information is available
874  *
875  * @param cls the closure from GNUNET_TESTBED_peer_get_information()
876  * @param op the operation this callback corresponds to
877  * @param pinfo the result; will be NULL if the operation has failed
878  * @param emsg error message if the operation has failed;
879  *             NULL if the operation is successfull
880  */
881 static void
882 pi_cb (void *cls,
883        struct GNUNET_TESTBED_Operation *op,
884        const struct GNUNET_TESTBED_PeerInformation *pinfo,
885        const char *emsg)
886 {
887   long i = (long) cls;
888
889   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "id callback for %ld\n", i);
890
891   if (NULL == pinfo || NULL != emsg)
892   {
893     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "pi_cb: %s\n", emsg);
894     abort_test (__LINE__);
895     return;
896   }
897   p_id[i] = pinfo->result.id;
898   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  id: %s\n", GNUNET_i2s (p_id[i]));
899   p_ids++;
900   if (p_ids < 2)
901     return;
902   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got all IDs, starting test\n");
903   test_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
904                                             &do_test, NULL);
905 }
906
907 /**
908  * test main: start test when all peers are connected
909  *
910  * @param cls Closure.
911  * @param ctx Argument to give to GNUNET_CADET_TEST_cleanup on test end.
912  * @param num_peers Number of peers that are running.
913  * @param peers Array of peers.
914  * @param cadetes Handle to each of the CADETs of the peers.
915  */
916 static void
917 tmain (void *cls,
918        struct GNUNET_CADET_TEST_Context *ctx,
919        unsigned int num_peers,
920        struct GNUNET_TESTBED_Peer **peers,
921        struct GNUNET_CADET_Handle **cadets)
922 {
923   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test main\n");
924   ok = 0;
925   test_ctx = ctx;
926   peers_running = num_peers;
927   GNUNET_assert (peers_running == peers_requested);
928   testbed_peers = peers;
929   h1 = cadets[0];
930   h2 = cadets[num_peers - 1];
931   disconnect_task = GNUNET_SCHEDULER_add_delayed (SHORT_TIME,
932                                                   &disconnect_cadet_peers,
933                                                   (void *) __LINE__);
934   GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
935   t_op[0] = GNUNET_TESTBED_peer_get_information (peers[0],
936                                                  GNUNET_TESTBED_PIT_IDENTITY,
937                                                  &pi_cb, (void *) 0L);
938   t_op[1] = GNUNET_TESTBED_peer_get_information (peers[num_peers - 1],
939                                                  GNUNET_TESTBED_PIT_IDENTITY,
940                                                  &pi_cb, (void *) 1L);
941   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "requested peer ids\n");
942 }
943
944
945 /**
946  * Main: start test
947  */
948 int
949 main (int argc, char *argv[])
950 {
951   initialized = GNUNET_NO;
952   static uint32_t ports[2];
953   const char *config_file;
954
955   GNUNET_log_setup ("test", "DEBUG", NULL);
956   config_file = "test_cadet.conf";
957
958   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Start\n");
959
960   /* Find out requested size */
961   if (strstr (argv[0], "_2_") != NULL)
962   {
963     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "DIRECT CONNECTIONs\n");
964     peers_requested = 2;
965   }
966   else if (strstr (argv[0], "_5_") != NULL)
967   {
968     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "5 PEER LINE\n");
969     peers_requested = 5;
970   }
971   else
972   {
973     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "SIZE UNKNOWN, USING 2\n");
974     peers_requested = 2;
975   }
976
977   /* Find out requested test */
978   if (strstr (argv[0], "_forward") != NULL)
979   {
980     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "FORWARD\n");
981     test = FORWARD;
982     test_name = "unicast";
983     ok_goal = 4;
984   }
985   else if (strstr (argv[0], "_signal") != NULL)
986   {
987     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SIGNAL\n");
988     test = P2P_SIGNAL;
989     test_name = "signal";
990     ok_goal = 4;
991   }
992   else if (strstr (argv[0], "_speed_ack") != NULL)
993   {
994     /* Test is supposed to generate the following callbacks:
995      * 1 incoming channel (@dest)
996      * TOTAL_PACKETS received data packet (@dest)
997      * TOTAL_PACKETS received data packet (@orig)
998      * 1 received channel destroy (@dest)
999      */
1000     ok_goal = TOTAL_PACKETS * 2 + 2;
1001     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SPEED_ACK\n");
1002     test = SPEED_ACK;
1003     test_name = "speed ack";
1004   }
1005   else if (strstr (argv[0], "_speed") != NULL)
1006   {
1007     /* Test is supposed to generate the following callbacks:
1008      * 1 incoming channel (@dest)
1009      * 1 initial packet (@dest)
1010      * TOTAL_PACKETS received data packet (@dest)
1011      * 1 received data packet (@orig)
1012      * 1 received channel destroy (@dest)
1013      */
1014     ok_goal = TOTAL_PACKETS + 4;
1015     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SPEED\n");
1016     if (strstr (argv[0], "_reliable") != NULL)
1017     {
1018       test = SPEED_REL;
1019       test_name = "speed reliable";
1020       config_file = "test_cadet_drop.conf";
1021     }
1022     else
1023     {
1024       test = SPEED;
1025       test_name = "speed";
1026     }
1027   }
1028   else if (strstr (argv[0], "_keepalive") != NULL)
1029   {
1030     test = KEEPALIVE;
1031     /* Test is supposed to generate the following callbacks:
1032      * 1 incoming channel (@dest)
1033      * [wait]
1034      * 1 received channel destroy (@dest)
1035      */
1036     ok_goal = 2;
1037   }
1038   else
1039   {
1040     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "UNKNOWN\n");
1041     test = SETUP;
1042     ok_goal = 0;
1043   }
1044
1045   if (strstr (argv[0], "backwards") != NULL)
1046   {
1047     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "BACKWARDS (LEAF TO ROOT)\n");
1048     test_backwards = GNUNET_YES;
1049     GNUNET_asprintf (&test_name, "backwards %s", test_name);
1050   }
1051
1052   p_ids = 0;
1053   ports[0] = 1;
1054   ports[1] = 0;
1055   GNUNET_CADET_TEST_run ("test_cadet_small",
1056                         config_file,
1057                         peers_requested,
1058                         &tmain,
1059                         NULL, /* tmain cls */
1060                         &incoming_channel,
1061                         &channel_cleaner,
1062                         handlers,
1063                         ports);
1064
1065   if (ok_goal > ok)
1066   {
1067     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1068                 "FAILED! (%d/%d)\n", ok, ok_goal);
1069     return 1;
1070   }
1071   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "success\n");
1072   return 0;
1073 }
1074
1075 /* end of test_cadet.c */