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