- add framework for mesh2 mutipeer tests
[oweals/gnunet.git] / src / mesh / test_mesh2_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_mesh2_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_mesh2_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 SPEED 3
53 #define SPEED_ACK 4
54 #define SPEED_MIN 5
55 #define SPEED_NOBUF 6
56 #define P2P_SIGNAL 10
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   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ending test.\n");
237   shutdown_handle = GNUNET_SCHEDULER_NO_TASK;
238 }
239
240
241 /**
242  * Disconnect from mesh services af all peers, call shutdown.
243  * 
244  * @param cls Closure (unused).
245  * @param tc Task Context.
246  */
247 static void
248 disconnect_mesh_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
249 {
250   long line = (long) cls;
251   unsigned int i;
252
253   for (i = 0; i < 3; i++)
254     if (NULL != t_op[i])
255     {
256       GNUNET_TESTBED_operation_done (t_op[i]);
257       t_op[i] = NULL;
258     }
259   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
260               "disconnecting mesh service of peers, called from line %ld\n",
261               line);
262   disconnect_task = GNUNET_SCHEDULER_NO_TASK;
263   if (NULL != t)
264   {
265     GNUNET_MESH_tunnel_destroy (t);
266     t = NULL;
267   }
268   if (NULL != incoming_t)
269   {
270     GNUNET_MESH_tunnel_destroy (incoming_t);
271     incoming_t = NULL;
272   }
273   if (NULL != incoming_t2)
274   {
275     GNUNET_MESH_tunnel_destroy (incoming_t2);
276     incoming_t2 = NULL;
277   }
278   GNUNET_MESH_TEST_cleanup (test_ctx);
279   if (GNUNET_SCHEDULER_NO_TASK != shutdown_handle)
280   {
281     GNUNET_SCHEDULER_cancel (shutdown_handle);
282   }
283   shutdown_handle = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
284 }
285
286
287 /**
288  * Abort test: schedule disconnect and shutdown immediately
289  * 
290  * @param line Line in the code the abort is requested from (__LINE__).
291  */
292 void
293 abort_test (long line)
294 {
295   if (disconnect_task != GNUNET_SCHEDULER_NO_TASK)
296   {
297     GNUNET_SCHEDULER_cancel (disconnect_task);
298   }
299   disconnect_task = GNUNET_SCHEDULER_add_delayed (SHORT_TIME,
300                                                   &disconnect_mesh_peers,
301                                                   (void *) line);
302 }
303
304 /**
305  * Transmit ready callback.
306  * 
307  * @param cls Closure (message type).
308  * @param size Size of the tranmist buffer.
309  * @param buf Pointer to the beginning of the buffer.
310  * 
311  * @return Number of bytes written to buf.
312  */
313 static size_t
314 tmt_rdy (void *cls, size_t size, void *buf);
315
316
317 /**
318  * Task to schedule a new data transmission.
319  * 
320  * @param cls Closure (peer #).
321  * @param tc Task Context.
322  */
323 static void
324 data_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
325 {
326   struct GNUNET_MESH_TransmitHandle *th;
327   struct GNUNET_MESH_Tunnel *tunnel;
328   struct GNUNET_PeerIdentity *destination;
329
330   if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0)
331     return;
332
333   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Data task\n");
334   if (GNUNET_YES == test_backwards)
335   {
336     tunnel = incoming_t;
337     destination = p_id[0];
338   }
339   else
340   {
341     tunnel = t;
342     destination = p_id[2];
343   }
344   th = GNUNET_MESH_notify_transmit_ready (tunnel, GNUNET_NO,
345                                           GNUNET_TIME_UNIT_FOREVER_REL,
346                                           destination,
347                                           size_payload,
348                                           &tmt_rdy, (void *) 1L);
349   if (NULL == th)
350   {
351     unsigned long i = (unsigned long) cls;
352
353     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Retransmission\n");
354     if (0 == i)
355     {
356       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "  in 1 ms\n");
357       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
358                                     &data_task, (void *)1UL);
359     }
360     else
361     {
362       i++;
363       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "in %u ms\n", i);
364       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply(
365                                       GNUNET_TIME_UNIT_MILLISECONDS,
366                                       i),
367                                     &data_task, (void *)i);
368     }
369   }
370 }
371
372
373 /**
374  * Transmit ready callback
375  *
376  * @param cls Closure (message type).
377  * @param size Size of the buffer we have.
378  * @param buf Buffer to copy data to.
379  */
380 size_t
381 tmt_rdy (void *cls, size_t size, void *buf)
382 {
383   struct GNUNET_MessageHeader *msg = buf;
384   uint32_t *data;
385
386   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
387               " tmt_rdy called\n");
388   if (size < size_payload || NULL == buf)
389   {
390     GNUNET_break (0);
391     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
392                 "size %u, buf %p, data_sent %u, data_received %u\n",
393                 size,
394                 buf,
395                 data_sent,
396                 data_received);
397     return 0;
398   }
399   msg->size = htons (size);
400   msg->type = htons ((long) cls);
401   data = (uint32_t *) &msg[1];
402   *data = htonl (data_sent);
403   if (SPEED == test && GNUNET_YES == initialized)
404   {
405     data_sent++;
406     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
407               " Sent packet %d\n", data_sent);
408     if (data_sent < TOTAL_PACKETS)
409     {
410       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
411               " Scheduling packet %d\n", data_sent + 1);
412       GNUNET_SCHEDULER_add_now(&data_task, NULL);
413     }
414   }
415   return size_payload;
416 }
417
418
419 /**
420  * Function is called whenever a message is received.
421  *
422  * @param cls closure (set from GNUNET_MESH_connect)
423  * @param tunnel connection to the other end
424  * @param tunnel_ctx place to store local state associated with the tunnel
425  * @param sender who sent the message
426  * @param message the actual message
427  * @param atsi performance data for the connection
428  * @return GNUNET_OK to keep the connection open,
429  *         GNUNET_SYSERR to close it (signal serious error)
430  */
431 int
432 data_callback (void *cls, struct GNUNET_MESH_Tunnel *tunnel, void **tunnel_ctx,
433                const struct GNUNET_PeerIdentity *sender,
434                const struct GNUNET_MessageHeader *message,
435                const struct GNUNET_ATS_Information *atsi)
436 {
437   long client = (long) cls;
438   long expected_target_client;
439   uint32_t *data;
440
441   ok++;
442
443   if ((ok % 20) == 0)
444   {
445     if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
446     {
447       GNUNET_SCHEDULER_cancel (disconnect_task);
448     }
449     disconnect_task =
450               GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers,
451                                             (void *) __LINE__);
452   }
453
454   switch (client)
455   {
456   case 0L:
457     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Root client got a message!\n");
458     peers_responded++;
459     break;
460   case 3L:
461   case 4L:
462     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
463                 "Leaf client %li got a message.\n",
464                 client);
465     client = 4L;
466     break;
467   default:
468     GNUNET_assert (0);
469     break;
470   }
471   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: (%d/%d)\n", ok, ok_goal);
472   data = (uint32_t *) &message[1];
473   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " payload: (%u)\n", ntohl (*data));
474   if (SPEED == test && GNUNET_YES == test_backwards)
475   {
476     expected_target_client = 0L;
477   }
478   else
479   {
480     expected_target_client = 4L;
481   }
482
483   if (GNUNET_NO == initialized)
484   {
485     initialized = GNUNET_YES;
486     start_time = GNUNET_TIME_absolute_get ();
487     if (SPEED == test)
488     {
489       GNUNET_assert (4L == client);
490       GNUNET_SCHEDULER_add_now (&data_task, NULL);
491       return GNUNET_OK;
492     }
493   }
494
495   if (client == expected_target_client) // Normally 3 or 4
496   {
497     data_received++;
498     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
499                 " received data %u\n", data_received);
500     if (SPEED != test || (ok_goal - 2) == ok)
501     {
502       GNUNET_MESH_notify_transmit_ready (tunnel, GNUNET_NO,
503                                         GNUNET_TIME_UNIT_FOREVER_REL, sender,
504                                                size_payload,
505                                         &tmt_rdy, (void *) 1L);
506       return GNUNET_OK;
507     }
508     else
509     {
510       if (data_received < TOTAL_PACKETS)
511         return GNUNET_OK;
512     }
513   }
514   else // Normally 0
515   {
516     if (test == SPEED_ACK || test == SPEED)
517     {
518       data_ack++;
519       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
520               " received ack %u\n", data_ack);
521       GNUNET_MESH_notify_transmit_ready (tunnel, GNUNET_NO,
522                                         GNUNET_TIME_UNIT_FOREVER_REL, sender,
523                                                size_payload,
524                                         &tmt_rdy, (void *) 1L);
525       if (data_ack < TOTAL_PACKETS && SPEED != test)
526         return GNUNET_OK;
527       if (ok == 2 && SPEED == test)
528         return GNUNET_OK;
529       show_end_data();
530     }
531     if (test == P2P_SIGNAL)
532     {
533       GNUNET_MESH_tunnel_destroy (incoming_t);
534       incoming_t = NULL;
535     }
536     else
537     {
538       GNUNET_MESH_tunnel_destroy (t);
539       t = NULL;
540     }
541   }
542
543   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
544   {
545     GNUNET_SCHEDULER_cancel (disconnect_task);
546   }
547   disconnect_task =
548         GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers,
549                                       (void *) __LINE__);
550
551   return GNUNET_OK;
552 }
553
554
555 /**
556  * Handlers, for diverse services
557  */
558 static struct GNUNET_MESH_MessageHandler handlers[] = {
559   {&data_callback, 1, sizeof (struct GNUNET_MessageHeader)},
560   {NULL, 0, 0}
561 };
562
563
564 /**
565  * Method called whenever another peer has added us to a tunnel
566  * the other peer initiated.
567  *
568  * @param cls closure
569  * @param tunnel new handle to the tunnel
570  * @param initiator peer that started the tunnel
571  * @param atsi performance information for the tunnel
572  * @return initial tunnel context for the tunnel
573  *         (can be NULL -- that's not an error)
574  */
575 static void *
576 incoming_tunnel (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
577                  const struct GNUNET_PeerIdentity *initiator,
578                  const struct GNUNET_ATS_Information *atsi)
579 {
580   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
581               "Incoming tunnel from %s to peer %d\n",
582               GNUNET_i2s (initiator), (long) cls);
583   ok++;
584   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
585   if ((long) cls == 4L)
586     incoming_t = tunnel;
587   else if ((long) cls == 3L)
588     incoming_t2 = tunnel;
589   else
590   {
591     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
592                 "Incoming tunnel for unknown client %lu\n", (long) cls);
593     GNUNET_break(0);
594   }
595   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
596   {
597     GNUNET_SCHEDULER_cancel (disconnect_task);
598   }
599   disconnect_task =
600         GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers,
601                                       (void *) __LINE__);
602
603   return NULL;
604 }
605
606 /**
607  * Function called whenever an inbound tunnel is destroyed.  Should clean up
608  * any associated state.
609  *
610  * @param cls closure (set from GNUNET_MESH_connect)
611  * @param tunnel connection to the other end (henceforth invalid)
612  * @param tunnel_ctx place where local state associated
613  *                   with the tunnel is stored
614  */
615 static void
616 tunnel_cleaner (void *cls, const struct GNUNET_MESH_Tunnel *tunnel,
617                 void *tunnel_ctx)
618 {
619   long i = (long) cls;
620
621   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
622               "Incoming tunnel disconnected at peer %d\n",
623               i);
624   if (4L == i)
625   {
626     ok++;
627     incoming_t = NULL;
628   }
629   else if (3L == i)
630   {
631     ok++;
632     incoming_t2 = NULL;
633   }
634   else
635     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
636                 "Unknown peer! %d\n", i);
637   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
638   peers_in_tunnel--;
639   if (peers_in_tunnel > 0)
640     return;
641
642   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
643   {
644     GNUNET_SCHEDULER_cancel (disconnect_task);
645   }
646   disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_mesh_peers,
647                                               (void *) __LINE__);
648
649   return;
650 }
651
652
653 /**
654  * Method called whenever a tunnel falls apart.
655  *
656  * @param cls closure
657  * @param peer peer identity the tunnel stopped working with
658  */
659 static void
660 dh (void *cls, const struct GNUNET_PeerIdentity *peer)
661 {
662   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
663               "peer %s disconnected\n",
664               GNUNET_i2s (peer));
665   if (P2P_SIGNAL == test)
666   {
667     ok ++;
668     if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
669     {
670       GNUNET_SCHEDULER_cancel (disconnect_task);
671     }
672     disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_mesh_peers,
673                                                 (void *) __LINE__);
674   }
675   return;
676 }
677
678
679 /**
680  * Method called whenever a peer connects to a tunnel.
681  *
682  * @param cls closure
683  * @param peer peer identity the tunnel was created to, NULL on timeout
684  * @param atsi performance data for the connection
685  */
686 static void
687 ch (void *cls, const struct GNUNET_PeerIdentity *peer,
688     const struct GNUNET_ATS_Information *atsi)
689 {
690   long i = (long) cls;
691
692   struct GNUNET_PeerIdentity *dest;
693
694   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
695               "%ld peer %s connected\n", i, GNUNET_i2s (peer));
696
697   if (0 == memcmp (p_id[2], peer, sizeof (struct GNUNET_PeerIdentity)) &&
698       i == 0L)
699   {
700     ok++;
701   }
702   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
703   switch (test)
704   {
705     case UNICAST:
706     case P2P_SIGNAL:
707     case SPEED:
708     case SPEED_ACK:
709       // incoming_t is NULL unless we send a relevant data packet
710       dest = p_id[2];
711       break;
712     default:
713       GNUNET_assert (0);
714       return;
715   }
716   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
717   {
718     GNUNET_SCHEDULER_cancel (disconnect_task);
719     disconnect_task =
720         GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers,
721                                       (void *) __LINE__);
722     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
723                 "Sending data initializer...\n");
724     peers_responded = 0;
725     data_ack = 0;
726     data_received = 0;
727     data_sent = 0;
728     GNUNET_MESH_notify_transmit_ready (t, GNUNET_NO,
729                                        GNUNET_TIME_UNIT_FOREVER_REL, dest,
730                                            size_payload,
731                                        &tmt_rdy, (void *) 1L);
732   }
733   else
734   {
735     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
736                 "Disconnect already run?\n");
737     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
738                 "Aborting...\n");
739   }
740   return;
741 }
742
743
744 /**
745  * START THE TESTCASE ITSELF, AS WE ARE CONNECTED TO THE MESH SERVICES.
746  * 
747  * Testcase continues when the root receives confirmation of connected peers,
748  * on callback funtion ch.
749  * 
750  * @param cls Closure (unsued).
751  * @param tc Task Context.
752  */
753 static void
754 do_test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
755 {
756   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test_task\n");
757   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "add peer 2\n");
758   GNUNET_MESH_peer_request_connect_add (t, p_id[2]);
759
760   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
761               "schedule timeout in TIMEOUT\n");
762   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
763   {
764     GNUNET_SCHEDULER_cancel (disconnect_task);
765   }
766   disconnect_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
767                                                   &disconnect_mesh_peers,
768                                                   (void *) __LINE__);
769 }
770
771 /**
772  * Callback to be called when the requested peer information is available
773  *
774  * @param cls the closure from GNUNET_TESTBED_peer_get_information()
775  * @param op the operation this callback corresponds to
776  * @param pinfo the result; will be NULL if the operation has failed
777  * @param emsg error message if the operation has failed;
778  *             NULL if the operation is successfull
779  */
780 void
781 pi_cb (void *cls,
782        struct GNUNET_TESTBED_Operation *op,
783        const struct GNUNET_TESTBED_PeerInformation *pinfo,
784        const char *emsg)
785 {
786   long i = (long) cls;
787
788   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "id callback for %ld\n", i);
789   if (NULL == pinfo || NULL != emsg)
790   {
791     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "pi_cb: %s\n", emsg);
792     abort_test (__LINE__);
793     return;
794   }
795   p_id[i] = pinfo->result.id;
796   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  id: %s\n", GNUNET_i2s (p_id[i]));
797   p_ids++;
798   if (p_ids < 2)
799     return;
800   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got all IDs, starting test\n");
801   test_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
802                                             &do_test, NULL);
803 }
804
805 /**
806  * test main: start test when all peers are connected
807  *
808  * @param cls Closure.
809  * @param ctx Argument to give to GNUNET_MESH_TEST_cleanup on test end.
810  * @param num_peers Number of peers that are running.
811  * @param peers Array of peers.
812  * @param meshes Handle to each of the MESHs of the peers.
813  */
814 static void
815 tmain (void *cls,
816        struct GNUNET_MESH_TEST_Context *ctx,
817        unsigned int num_peers,
818        struct GNUNET_TESTBED_Peer **peers,
819        struct GNUNET_MESH_Handle **meshes)
820 {
821   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test main\n");
822   ok = 0;
823   test_ctx = ctx;
824   peers_running = num_peers;
825   h1 = meshes[0];
826   h2 = meshes[num_peers - 1];
827   t = GNUNET_MESH_tunnel_create (h1, NULL, &ch, &dh, (void *) 0L);
828   if (SPEED_MIN == test)
829   {
830     GNUNET_MESH_tunnel_speed_min(t);
831     test = SPEED;
832   }
833   if (SPEED_NOBUF == test)
834   {
835     GNUNET_MESH_tunnel_buffer(t, GNUNET_NO);
836     test = SPEED;
837   }
838   peers_in_tunnel = 0;
839   disconnect_task = GNUNET_SCHEDULER_add_delayed (SHORT_TIME,
840                                                   &disconnect_mesh_peers,
841                                                   (void *) __LINE__);
842   shutdown_handle = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
843                                                   &shutdown_task, NULL);
844   t_op[0] = GNUNET_TESTBED_peer_get_information (peers[0],
845                                                  GNUNET_TESTBED_PIT_IDENTITY,
846                                                  &pi_cb, (void *) 0L);
847   t_op[2] = GNUNET_TESTBED_peer_get_information (peers[num_peers - 1],
848                                                  GNUNET_TESTBED_PIT_IDENTITY,
849                                                  &pi_cb, (void *) 2L);
850   t_op[1] = NULL;
851   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "requested peer ids\n");
852 }
853
854
855 /**
856  * Main: start test
857  */
858 int
859 main (int argc, char *argv[])
860 {
861   initialized = GNUNET_NO;
862
863   GNUNET_log_setup ("test", "DEBUG", NULL);
864
865   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Start\n");
866   if (strstr (argv[0], "test_mesh2_small_forward") != NULL)
867   {
868     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "UNICAST\n");
869     test = UNICAST;
870     test_name = "unicast";
871     ok_goal = 5;
872   }
873   else if (strstr (argv[0], "test_mesh_small_signal") != NULL)
874   {
875     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SIGNAL\n");
876     test = P2P_SIGNAL;
877     test_name = "signal";
878     ok_goal = 5;
879   }
880   else if (strstr (argv[0], "test_mesh_small_speed_ack") != NULL)
881   {
882    /* Each peer is supposed to generate the following callbacks:
883     * 1 incoming tunnel (@dest)
884     * 1 connected peer (@orig)
885     * TOTAL_PACKETS received data packet (@dest)
886     * TOTAL_PACKETS received data packet (@orig)
887     * 1 received tunnel destroy (@dest)
888     * _________________________________
889     * 5 x ok expected per peer
890     */
891     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SPEED_ACK\n");
892     test = SPEED_ACK;
893     test_name = "speed ack";
894     ok_goal = TOTAL_PACKETS * 2 + 3;
895   }
896   else if (strstr (argv[0], "test_mesh_small_speed") != NULL)
897   {
898    /* Each peer is supposed to generate the following callbacks:
899     * 1 incoming tunnel (@dest)
900     * 1 connected peer (@orig)
901     * 1 initial packet (@dest)
902     * TOTAL_PACKETS received data packet (@dest)
903     * 1 received data packet (@orig)
904     * 1 received tunnel destroy (@dest)
905     * _________________________________
906     */
907     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SPEED\n");
908     ok_goal = TOTAL_PACKETS + 5;
909     if (strstr (argv[0], "_min") != NULL)
910     {
911       test = SPEED_MIN;
912       test_name = "speed min";
913     }
914     else if (strstr (argv[0], "_nobuf") != NULL)
915     {
916       test = SPEED_NOBUF;
917       test_name = "speed nobuf";
918     }
919     else
920     {
921       test = SPEED;
922       test_name = "speed";
923     }
924   }
925   else
926   {
927     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "UNKNOWN\n");
928     test = SETUP;
929     ok_goal = 0;
930   }
931
932   if (strstr (argv[0], "backwards") != NULL)
933   {
934     char *aux;
935
936     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "BACKWARDS (LEAF TO ROOT)\n");
937     test_backwards = GNUNET_YES;
938     aux = GNUNET_malloc (32);
939     sprintf (aux, "backwards %s", test_name);
940     test_name = aux;
941   }
942
943   p_ids = 0;
944   GNUNET_MESH_TEST_run ("test_mesh_small",
945                         "test_mesh_small.conf",
946                         5,
947                         &tmain,
948                         NULL,
949                         &incoming_tunnel,
950                         &tunnel_cleaner,
951                         handlers,
952                         NULL);
953
954   if (ok_goal > ok)
955   {
956     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
957                 "FAILED! (%d/%d)\n", ok, ok_goal);
958     return 1;
959   }
960   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "success\n");
961   return 0;
962 }
963
964 /* end of test_mesh_small.c */