- use consistent numbering
[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 <gauger.h>
30
31
32 /**
33  * How namy messages to send
34  */
35 #define TOTAL_PACKETS 1000
36
37 /**
38  * How long until we give up on connecting the peers?
39  */
40 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120)
41
42 /**
43  * Time to wait for stuff that should be rather fast
44  */
45 #define SHORT_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 20)
46
47 /**
48  * DIFFERENT TESTS TO RUN
49  */
50 #define SETUP 0
51 #define UNICAST 1
52 #define MULTICAST 2
53 #define SPEED 3
54 #define SPEED_ACK 4
55 #define SPEED_MIN 5
56 #define SPEED_NOBUF 6
57
58 /**
59  * Which test are we running?
60  */
61 static int test;
62
63 /**
64  * String with test name
65  */
66 char *test_name;
67
68 /**
69  * Flag to send traffic leaf->root in speed tests to test BCK_ACK logic.
70  */
71 static int test_backwards = GNUNET_NO;
72
73 /**
74  * How many events have happened
75  */
76 static int ok;
77
78  /**
79   * Each peer is supposed to generate the following callbacks:
80   * 1 incoming tunnel (@dest)
81   * 1 connected peer (@orig)
82   * 1 received data packet (@dest)
83   * 1 received data packet (@orig)
84   * 1 received tunnel destroy (@dest)
85   * _________________________________
86   * 5 x ok expected per peer
87   */
88 int ok_goal;
89
90
91 /**
92  * Size of each test packet
93  */
94 size_t size_payload = sizeof (struct GNUNET_MessageHeader) + sizeof (uint32_t);
95
96 /**
97  * Operation to get peer ids.
98  */
99 struct GNUNET_TESTBED_Operation *t_op[3];
100
101 /**
102  * Peer ids.
103  */
104 struct GNUNET_PeerIdentity *p_id[3];
105
106 /**
107  * Peer ids counter.
108  */
109 unsigned int p_ids;
110
111 /**
112  * Is the setup initialized?
113  */
114 static int initialized;
115
116 /**
117  * Peers that have been connected
118  */
119 static int peers_in_tunnel;
120
121 /**
122  * Peers that have responded
123  */
124 static int peers_responded;
125
126 /**
127  * Number of payload packes sent
128  */
129 static int data_sent;
130
131 /**
132  * Number of payload packets received
133  */
134 static int data_received;
135
136 /**
137  * Number of payload packed explicitly (app level) acknowledged
138  */
139 static int data_ack;
140
141 /**
142  * Total number of currently running peers.
143  */
144 static unsigned long long peers_running;
145
146 /**
147  * Test context (to shut down).
148  */
149 struct GNUNET_MESH_TEST_Context *test_ctx;
150
151 /**
152  * Task called to disconnect peers.
153  */
154 static GNUNET_SCHEDULER_TaskIdentifier disconnect_task;
155
156 /**
157  * Task To perform tests
158  */
159 static GNUNET_SCHEDULER_TaskIdentifier test_task;
160
161 /**
162  * Task called to shutdown test.
163  */
164 static GNUNET_SCHEDULER_TaskIdentifier shutdown_handle;
165
166 /**
167  * Mesh handle for the root peer
168  */
169 static struct GNUNET_MESH_Handle *h1;
170
171 /**
172  * Mesh handle for the first leaf peer
173  */
174 static struct GNUNET_MESH_Handle *h2;
175
176 /**
177  * Mesh handle for the second leaf peer
178  */
179 static struct GNUNET_MESH_Handle *h3;
180
181 /**
182  * Tunnel handle for the root peer
183  */
184 static struct GNUNET_MESH_Tunnel *t;
185
186 /**
187  * Tunnel handle for the first leaf peer
188  */
189 static struct GNUNET_MESH_Tunnel *incoming_t;
190
191 /**
192  * Tunnel handle for the second leaf peer
193  */
194 static struct GNUNET_MESH_Tunnel *incoming_t2;
195
196 /**
197  * Time we started the data transmission (after tunnel has been established
198  * and initilized).
199  */
200 static struct GNUNET_TIME_Absolute start_time;
201
202
203 /**
204  * Show the results of the test (banwidth acheived) and log them to GAUGER
205  */
206 static void
207 show_end_data (void)
208 {
209   static struct GNUNET_TIME_Absolute end_time;
210   static struct GNUNET_TIME_Relative total_time;
211
212   end_time = GNUNET_TIME_absolute_get();
213   total_time = GNUNET_TIME_absolute_get_difference(start_time, end_time);
214   FPRINTF (stderr, "\nResults of test \"%s\"\n", test_name);
215   FPRINTF (stderr, "Test time %llu ms\n",
216             (unsigned long long) total_time.rel_value);
217   FPRINTF (stderr, "Test bandwidth: %f kb/s\n",
218             4 * TOTAL_PACKETS * 1.0 / total_time.rel_value); // 4bytes * ms
219   FPRINTF (stderr, "Test throughput: %f packets/s\n\n",
220             TOTAL_PACKETS * 1000.0 / total_time.rel_value); // packets * ms
221   GAUGER ("MESH", test_name,
222           TOTAL_PACKETS * 1000.0 / total_time.rel_value,
223           "packets/s");
224 }
225
226
227 /**
228  * Shut down peergroup, clean up.
229  * 
230  * @param cls Closure (unused).
231  * @param tc Task Context.
232  */
233 static void
234 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
235 {
236   fprintf(stderr, "b");
237   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ending test.\n");
238   shutdown_handle = GNUNET_SCHEDULER_NO_TASK;
239 }
240
241
242 /**
243  * Disconnect from mesh services af all peers, call shutdown.
244  * 
245  * @param cls Closure (unused).
246  * @param tc Task Context.
247  */
248 static void
249 disconnect_mesh_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
250 {
251   long line = (long) cls;
252   unsigned int i;
253
254   fprintf(stderr, "a");
255   for (i = 0; i < 3; i++)
256     if (NULL != t_op[i])
257     {
258       GNUNET_TESTBED_operation_done (t_op[i]);
259       t_op[i] = NULL;
260     }
261   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
262               "disconnecting mesh service of peers, called from line %ld\n",
263               line);
264   disconnect_task = GNUNET_SCHEDULER_NO_TASK;
265   if (NULL != t)
266   {
267     GNUNET_MESH_tunnel_destroy (t);
268     t = NULL;
269   }
270   if (NULL != incoming_t)
271   {
272     GNUNET_MESH_tunnel_destroy (incoming_t);
273     incoming_t = NULL;
274   }
275   if (NULL != incoming_t2)
276   {
277     GNUNET_MESH_tunnel_destroy (incoming_t2);
278     incoming_t2 = NULL;
279   }
280   GNUNET_MESH_TEST_cleanup (test_ctx);
281   if (GNUNET_SCHEDULER_NO_TASK != shutdown_handle)
282   {
283     GNUNET_SCHEDULER_cancel (shutdown_handle);
284   }
285   shutdown_handle = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
286 }
287
288
289 void
290 abort_test (long line)
291 {
292   if (disconnect_task != GNUNET_SCHEDULER_NO_TASK)
293   {
294     GNUNET_SCHEDULER_cancel (disconnect_task);
295   }
296     disconnect_task = GNUNET_SCHEDULER_add_delayed (SHORT_TIME,
297                                                     &disconnect_mesh_peers,
298                                                     (void *) line);
299 }
300
301 /**
302  * Transmit ready callback.
303  * 
304  * @param cls Closure (message type).
305  * @param size Size of the tranmist buffer.
306  * @param buf Pointer to the beginning of the buffer.
307  * 
308  * @return Number of bytes written to buf.
309  */
310 static size_t
311 tmt_rdy (void *cls, size_t size, void *buf);
312
313
314 /**
315  * Task to schedule a new data transmission.
316  * 
317  * @param cls Closure (peer #).
318  * @param tc Task Context.
319  */
320 static void
321 data_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
322 {
323   struct GNUNET_MESH_TransmitHandle *th;
324   struct GNUNET_MESH_Tunnel *tunnel;
325   struct GNUNET_PeerIdentity *destination;
326
327   if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0)
328     return;
329
330   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Data task\n");
331   if (GNUNET_YES == test_backwards)
332   {
333     tunnel = incoming_t;
334     destination = p_id[0];
335   }
336   else
337   {
338     tunnel = t;
339     destination = p_id[2];
340   }
341   th = GNUNET_MESH_notify_transmit_ready (tunnel, GNUNET_NO,
342                                           GNUNET_TIME_UNIT_FOREVER_REL,
343                                           destination,
344                                           size_payload,
345                                           &tmt_rdy, (void *) 1L);
346   if (NULL == th)
347   {
348     unsigned long i = (unsigned long) cls;
349
350     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Retransmission\n");
351     if (0 == i)
352     {
353       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "  in 1 ms\n");
354       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
355                                     &data_task, (void *)1UL);
356     }
357     else
358     {
359       i++;
360       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "in %u ms\n", i);
361       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply(
362                                       GNUNET_TIME_UNIT_MILLISECONDS,
363                                       i),
364                                     &data_task, (void *)i);
365     }
366   }
367 }
368
369
370 /**
371  * Transmit ready callback
372  *
373  * @param cls Closure (message type).
374  * @param size Size of the buffer we have.
375  * @param buf Buffer to copy data to.
376  */
377 size_t
378 tmt_rdy (void *cls, size_t size, void *buf)
379 {
380   struct GNUNET_MessageHeader *msg = buf;
381   uint32_t *data;
382
383   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
384               " tmt_rdy called\n");
385   if (size < size_payload || NULL == buf)
386   {
387     GNUNET_break (0);
388     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
389                 "size %u, buf %p, data_sent %u, data_received %u\n",
390                 size,
391                 buf,
392                 data_sent,
393                 data_received);
394     return 0;
395   }
396   msg->size = htons (size);
397   msg->type = htons ((long) cls);
398   data = (uint32_t *) &msg[1];
399   *data = htonl (data_sent);
400   if (SPEED == test && GNUNET_YES == initialized)
401   {
402     data_sent++;
403     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
404               " Sent packet %d\n", data_sent);
405     if (data_sent < TOTAL_PACKETS)
406     {
407       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
408               " Scheduling packet %d\n", data_sent + 1);
409       GNUNET_SCHEDULER_add_now(&data_task, NULL);
410     }
411   }
412   return size_payload;
413 }
414
415
416 /**
417  * Function is called whenever a message is received.
418  *
419  * @param cls closure (set from GNUNET_MESH_connect)
420  * @param tunnel connection to the other end
421  * @param tunnel_ctx place to store local state associated with the tunnel
422  * @param sender who sent the message
423  * @param message the actual message
424  * @param atsi performance data for the connection
425  * @return GNUNET_OK to keep the connection open,
426  *         GNUNET_SYSERR to close it (signal serious error)
427  */
428 int
429 data_callback (void *cls, struct GNUNET_MESH_Tunnel *tunnel, void **tunnel_ctx,
430                const struct GNUNET_PeerIdentity *sender,
431                const struct GNUNET_MessageHeader *message,
432                const struct GNUNET_ATS_Information *atsi)
433 {
434   long client = (long) cls;
435   long expected_target_client;
436   uint32_t *data;
437
438   ok++;
439
440   if ((ok % 20) == 0)
441   {
442     if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
443     {
444       GNUNET_SCHEDULER_cancel (disconnect_task);
445     }
446     disconnect_task =
447               GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers,
448                                             (void *) __LINE__);
449   }
450
451   switch (client)
452   {
453   case 0L:
454     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Root client got a message!\n");
455     peers_responded++;
456     if (test == MULTICAST && peers_responded < 2)
457       return GNUNET_OK;
458     break;
459   case 3L:
460   case 4L:
461     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
462                 "Leaf client %li got a message.\n",
463                 client);
464     client = 4L;
465     break;
466   default:
467     GNUNET_assert (0);
468     break;
469   }
470   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: (%d/%d)\n", ok, ok_goal);
471   data = (uint32_t *) &message[1];
472   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " payload: (%u)\n", ntohl (*data));
473   if (SPEED == test && GNUNET_YES == test_backwards)
474   {
475     expected_target_client = 0L;
476   }
477   else
478   {
479     expected_target_client = 4L;
480   }
481
482   if (GNUNET_NO == initialized)
483   {
484     initialized = GNUNET_YES;
485     start_time = GNUNET_TIME_absolute_get ();
486     if (SPEED == test)
487     {
488       GNUNET_assert (4L == client);
489       GNUNET_SCHEDULER_add_now (&data_task, NULL);
490       return GNUNET_OK;
491     }
492   }
493
494   if (client == expected_target_client) // Normally 3 or 4
495   {
496     data_received++;
497     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
498                 " received data %u\n", data_received);
499     if (SPEED != test || (ok_goal - 2) == ok)
500     {
501       GNUNET_MESH_notify_transmit_ready (tunnel, GNUNET_NO,
502                                         GNUNET_TIME_UNIT_FOREVER_REL, sender,
503                                                size_payload,
504                                         &tmt_rdy, (void *) 1L);
505       return GNUNET_OK;
506     }
507     else
508     {
509       if (data_received < TOTAL_PACKETS)
510         return GNUNET_OK;
511     }
512   }
513   else // Normally 0
514   {
515     if (test == SPEED_ACK || test == SPEED)
516     {
517       data_ack++;
518       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
519               " received ack %u\n", data_ack);
520       GNUNET_MESH_notify_transmit_ready (tunnel, GNUNET_NO,
521                                         GNUNET_TIME_UNIT_FOREVER_REL, sender,
522                                                size_payload,
523                                         &tmt_rdy, (void *) 1L);
524       if (data_ack < TOTAL_PACKETS && SPEED != test)
525         return GNUNET_OK;
526       if (ok == 2 && SPEED == test)
527         return GNUNET_OK;
528       show_end_data();
529     }
530     GNUNET_MESH_tunnel_destroy (t);
531     t = NULL;
532   }
533
534   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
535   {
536     GNUNET_SCHEDULER_cancel (disconnect_task);
537   }
538   disconnect_task =
539         GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers,
540                                       (void *) __LINE__);
541
542   return GNUNET_OK;
543 }
544
545
546 /**
547  * Handlers, for diverse services
548  */
549 static struct GNUNET_MESH_MessageHandler handlers[] = {
550   {&data_callback, 1, sizeof (struct GNUNET_MessageHeader)},
551   {NULL, 0, 0}
552 };
553
554
555 /**
556  * Method called whenever another peer has added us to a tunnel
557  * the other peer initiated.
558  *
559  * @param cls closure
560  * @param tunnel new handle to the tunnel
561  * @param initiator peer that started the tunnel
562  * @param atsi performance information for the tunnel
563  * @return initial tunnel context for the tunnel
564  *         (can be NULL -- that's not an error)
565  */
566 static void *
567 incoming_tunnel (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
568                  const struct GNUNET_PeerIdentity *initiator,
569                  const struct GNUNET_ATS_Information *atsi)
570 {
571   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
572               "Incoming tunnel from %s to peer %d\n",
573               GNUNET_i2s (initiator), (long) cls);
574   ok++;
575   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
576   if ((long) cls == 4L)
577     incoming_t = tunnel;
578   else if ((long) cls == 3L)
579     incoming_t2 = tunnel;
580   else
581   {
582     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
583                 "Incoming tunnel for unknown client %lu\n", (long) cls);
584     GNUNET_break(0);
585   }
586   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
587   {
588     GNUNET_SCHEDULER_cancel (disconnect_task);
589   }
590   disconnect_task =
591         GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers,
592                                       (void *) __LINE__);
593
594   return NULL;
595 }
596
597 /**
598  * Function called whenever an inbound tunnel is destroyed.  Should clean up
599  * any associated state.
600  *
601  * @param cls closure (set from GNUNET_MESH_connect)
602  * @param tunnel connection to the other end (henceforth invalid)
603  * @param tunnel_ctx place where local state associated
604  *                   with the tunnel is stored
605  */
606 static void
607 tunnel_cleaner (void *cls, const struct GNUNET_MESH_Tunnel *tunnel,
608                 void *tunnel_ctx)
609 {
610   long i = (long) cls;
611
612   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
613               "Incoming tunnel disconnected at peer %d\n",
614               i);
615   if (4L == i)
616   {
617     ok++;
618     incoming_t = NULL;
619   }
620   else if (3L == i)
621   {
622     ok++;
623     incoming_t2 = NULL;
624   }
625   else
626     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
627                 "Unknown peer! %d\n", i);
628   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
629   peers_in_tunnel--;
630   if (peers_in_tunnel > 0)
631     return;
632
633   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
634   {
635     GNUNET_SCHEDULER_cancel (disconnect_task);
636   }
637   disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_mesh_peers,
638                                               (void *) __LINE__);
639
640   return;
641 }
642
643
644 /**
645  * Method called whenever a tunnel falls apart.
646  *
647  * @param cls closure
648  * @param peer peer identity the tunnel stopped working with
649  */
650 static void
651 dh (void *cls, const struct GNUNET_PeerIdentity *peer)
652 {
653   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
654               "peer %s disconnected\n",
655               GNUNET_i2s (peer));
656   return;
657 }
658
659
660 /**
661  * Method called whenever a peer connects to a tunnel.
662  *
663  * @param cls closure
664  * @param peer peer identity the tunnel was created to, NULL on timeout
665  * @param atsi performance data for the connection
666  */
667 static void
668 ch (void *cls, const struct GNUNET_PeerIdentity *peer,
669     const struct GNUNET_ATS_Information *atsi)
670 {
671   long i = (long) cls;
672
673   struct GNUNET_PeerIdentity *dest;
674
675   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
676               "peer %s connected\n", GNUNET_i2s (peer));
677
678   if (0 == memcmp (p_id[2], peer, sizeof (struct GNUNET_PeerIdentity)) &&
679       i == 0L)
680   {
681     ok++;
682   }
683   if (test == MULTICAST &&
684       0 == memcmp (p_id[1], peer, sizeof (struct GNUNET_PeerIdentity)) &&
685       i == 0L)
686   {
687     ok++;
688   }
689   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
690   switch (test)
691   {
692     case UNICAST:
693     case SPEED:
694     case SPEED_ACK:
695       // incoming_t is NULL unless we send a relevant data packet
696       dest = p_id[2];
697       break;
698     case MULTICAST:
699       peers_in_tunnel++;
700       if (peers_in_tunnel < 2)
701         return;
702       dest = NULL;
703       break;
704     default:
705       GNUNET_assert (0);
706       return;
707   }
708   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
709   {
710     GNUNET_SCHEDULER_cancel (disconnect_task);
711     disconnect_task =
712         GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers,
713                                       (void *) __LINE__);
714     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
715                 "Sending data initializer...\n");
716     peers_responded = 0;
717     data_ack = 0;
718     data_received = 0;
719     data_sent = 0;
720     GNUNET_MESH_notify_transmit_ready (t, GNUNET_NO,
721                                        GNUNET_TIME_UNIT_FOREVER_REL, dest,
722                                            size_payload,
723                                        &tmt_rdy, (void *) 1L);
724   }
725   else
726   {
727     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
728                 "Disconnect already run?\n");
729     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
730                 "Aborting...\n");
731   }
732   return;
733 }
734
735
736 /**
737  * START THE TESTCASE ITSELF, AS WE ARE CONNECTED TO THE MESH SERVICES.
738  * 
739  * Testcase continues when the root receives confirmation of connected peers,
740  * on callback funtion ch.
741  * 
742  * @param cls Closure (unsued).
743  * @param tc Task Context.
744  */
745 static void
746 do_test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
747 {
748   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test_task\n");
749   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "add peer 2\n");
750   GNUNET_MESH_peer_request_connect_add (t, p_id[2]);
751
752   if (test == MULTICAST)
753   {
754     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
755                 "add peer 3\n");
756     GNUNET_MESH_peer_request_connect_add (t, p_id[1]);
757   }
758
759   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
760               "schedule timeout in TIMEOUT\n");
761   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
762   {
763     GNUNET_SCHEDULER_cancel (disconnect_task);
764     disconnect_task =
765         GNUNET_SCHEDULER_add_delayed (TIMEOUT, &disconnect_mesh_peers,
766                                       (void *) __LINE__);
767   }
768 }
769
770 /**
771  * Callback to be called when the requested peer information is available
772  *
773  * @param cls the closure from GNUNET_TETSBED_peer_get_information()
774  * @param op the operation this callback corresponds to
775  * @param pinfo the result; will be NULL if the operation has failed
776  * @param emsg error message if the operation has failed;
777  *             NULL if the operation is successfull
778  */
779 void
780 pi_cb (void *cls,
781        struct GNUNET_TESTBED_Operation *op,
782        const struct GNUNET_TESTBED_PeerInformation *pinfo,
783        const char *emsg)
784 {
785   long i = (long) cls;
786
787   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "id callback for %ld\n", i);
788   if (NULL == pinfo || NULL != emsg)
789   {
790     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "pi_cb: %s\n", emsg);
791     abort_test (__LINE__);
792     return;
793   }
794   p_id[i] = pinfo->result.id;
795   p_ids++;
796   if ((MULTICAST == test && p_ids < 3) || p_ids < 2)
797     return;
798   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got all IDs, starting test\n");
799   test_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
800                                             &do_test, NULL);
801 }
802
803 /**
804  * test main: start test when all peers are connected
805  *
806  * @param cls Closure.
807  * @param ctx Argument to give to GNUNET_MESH_TEST_cleanup on test end.
808  * @param num_peers Number of peers that are running.
809  * @param peers Array of peers.
810  * @param meshes Handle to each of the MESHs of the peers.
811  */
812 static void
813 tmain (void *cls,
814        struct GNUNET_MESH_TEST_Context *ctx,
815        unsigned int num_peers,
816        struct GNUNET_TESTBED_Peer **peers,
817        struct GNUNET_MESH_Handle **meshes)
818 {
819   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test main\n");
820   ok = 0;
821   test_ctx = ctx;
822   peers_running = num_peers;
823   h1 = meshes[0];
824   h2 = meshes[num_peers - 1];
825   t = GNUNET_MESH_tunnel_create (h1, NULL, &ch, &dh, (void *) 1L);
826   if (SPEED_MIN == test)
827   {
828     GNUNET_MESH_tunnel_speed_min(t);
829     test = SPEED;
830   }
831   if (SPEED_NOBUF == test)
832   {
833     GNUNET_MESH_tunnel_buffer(t, GNUNET_NO);
834     test = SPEED;
835   }
836   peers_in_tunnel = 0;
837   disconnect_task = GNUNET_SCHEDULER_add_delayed (SHORT_TIME,
838                                                   &disconnect_mesh_peers,
839                                                   (void *) __LINE__);
840   shutdown_handle = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
841                                                   &shutdown_task, NULL);
842   t_op[0] = GNUNET_TESTBED_peer_get_information (peers[0],
843                                                  GNUNET_TESTBED_PIT_IDENTITY,
844                                                  &pi_cb, (void *) 0L);
845   t_op[2] = GNUNET_TESTBED_peer_get_information (peers[num_peers - 1],
846                                                  GNUNET_TESTBED_PIT_IDENTITY,
847                                                  &pi_cb, (void *) 2L);
848   if (MULTICAST == test)
849   {
850     h3 = meshes[num_peers - 2];
851     t_op[1] = GNUNET_TESTBED_peer_get_information (peers[num_peers - 2],
852                                                    GNUNET_TESTBED_PIT_IDENTITY,
853                                                    &pi_cb, (void *) 1L);
854   }
855   else
856   {
857     t_op[1] = NULL;
858   }
859   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "requested peer ids\n");
860 }
861
862
863 /**
864  * Main: start test
865  */
866 int
867 main (int argc, char *argv[])
868 {
869   initialized = GNUNET_NO;
870
871   GNUNET_log_setup ("test", "DEBUG", NULL);
872   
873   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Start\n");
874   if (strstr (argv[0], "test_mesh_small_unicast") != NULL)
875   {
876     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "UNICAST\n");
877     test = UNICAST;
878     test_name = "unicast";
879     ok_goal = 5;
880   }
881   else if (strstr (argv[0], "test_mesh_small_multicast") != NULL)
882   {
883     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MULTICAST\n");
884     test = MULTICAST;
885     test_name = "multicast";
886     ok_goal = 10;
887   }
888   else if (strstr (argv[0], "test_mesh_small_speed_ack") != NULL)
889   {
890    /* Each peer is supposed to generate the following callbacks:
891     * 1 incoming tunnel (@dest)
892     * 1 connected peer (@orig)
893     * TOTAL_PACKETS received data packet (@dest)
894     * TOTAL_PACKETS received data packet (@orig)
895     * 1 received tunnel destroy (@dest)
896     * _________________________________
897     * 5 x ok expected per peer
898     */
899     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SPEED_ACK\n");
900     test = SPEED_ACK;
901     test_name = "speed ack";
902     ok_goal = TOTAL_PACKETS * 2 + 3;
903   }
904   else if (strstr (argv[0], "test_mesh_small_speed") != NULL)
905   {
906    /* Each peer is supposed to generate the following callbacks:
907     * 1 incoming tunnel (@dest)
908     * 1 connected peer (@orig)
909     * 1 initial packet (@dest)
910     * TOTAL_PACKETS received data packet (@dest)
911     * 1 received data packet (@orig)
912     * 1 received tunnel destroy (@dest)
913     * _________________________________
914     */
915     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SPEED\n");
916     ok_goal = TOTAL_PACKETS + 5;
917     if (strstr (argv[0], "_min") != NULL)
918     {
919       test = SPEED_MIN;
920       test_name = "speed min";
921     }
922     else if (strstr (argv[0], "_nobuf") != NULL)
923     {
924       test = SPEED_NOBUF;
925       test_name = "speed nobuf";
926     }
927     else
928     {
929       test = SPEED;
930       test_name = "speed";
931     }
932   }
933   else
934   {
935     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "UNKNOWN\n");
936     test = SETUP;
937     ok_goal = 0;
938   }
939
940   if (strstr (argv[0], "backwards") != NULL)
941   {
942     char *aux;
943
944     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "BACKWARDS (LEAF TO ROOT)\n");
945     test_backwards = GNUNET_YES;
946     aux = GNUNET_malloc (32);
947     sprintf (aux, "backwards %s", test_name);
948     test_name = aux;
949   }
950
951   p_ids = 0;
952   GNUNET_MESH_TEST_run (test_name,
953                         "test_mesh_small.conf",
954                         5,
955                         &tmain,
956                         NULL,
957                         &incoming_tunnel,
958                         &tunnel_cleaner,
959                         handlers,
960                         NULL);
961
962   if (ok_goal > ok)
963   {
964     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
965                 "FAILED! (%d/%d)\n", ok, ok_goal);
966     return 1;
967   }
968   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "success\n");
969   return 0;
970 }
971
972 /* end of test_mesh_small.c */