- notify clients when a tunnel times out
[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 "gnunet_testing_lib.h"
28 #include "gnunet_mesh_service.h"
29 #include <gauger.h>
30
31
32 #define VERBOSE GNUNET_YES
33 #define REMOVE_DIR GNUNET_YES
34
35 struct MeshPeer
36 {
37   struct MeshPeer *prev;
38
39   struct MeshPeer *next;
40
41   struct GNUNET_TESTING_Daemon *daemon;
42
43   struct GNUNET_MESH_Handle *mesh_handle;
44 };
45
46
47 /**
48  * How long until we give up on connecting the peers?
49  */
50 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120)
51
52 /**
53  * Time to wait for stuff that should be rather fast
54  */
55 #define SHORT_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
56
57 /**
58  * DIFFERENT TESTS TO RUN
59  */
60 #define SETUP 0
61 #define UNICAST 1
62 #define MULTICAST 2
63 #define SPEED 3
64 #define SPEED_ACK 4
65 #define SPEED_MIN 5
66 #define SPEED_NOBUF 6
67
68 /**
69  * Which test are we running?
70  */
71 static int test;
72
73 /**
74  * How many events have happened
75  */
76 static int ok;
77
78 static int peers_in_tunnel;
79
80 static int peers_responded;
81
82 static int data_sent;
83
84 static int data_received;
85
86 static int data_ack;
87
88 /**
89  * Be verbose
90  */
91 static int verbose;
92
93 /**
94  * Total number of peers in the test.
95  */
96 static unsigned long long num_peers;
97
98 /**
99  * Global configuration file
100  */
101 static struct GNUNET_CONFIGURATION_Handle *testing_cfg;
102
103 /**
104  * Total number of currently running peers.
105  */
106 static unsigned long long peers_running;
107
108 /**
109  * Total number of connections in the whole network.
110  */
111 static unsigned int total_connections;
112
113 /**
114  * The currently running peer group.
115  */
116 static struct GNUNET_TESTING_PeerGroup *pg;
117
118 /**
119  * File to report results to.
120  */
121 static struct GNUNET_DISK_FileHandle *output_file;
122
123 /**
124  * File to log connection info, statistics to.
125  */
126 static struct GNUNET_DISK_FileHandle *data_file;
127
128 /**
129  * Wait time
130  */
131 static struct GNUNET_TIME_Relative wait_time;
132
133 /**
134  * Task called to disconnect peers.
135  */
136 static GNUNET_SCHEDULER_TaskIdentifier disconnect_task;
137
138 /**
139  * Task To perform tests
140  */
141 static GNUNET_SCHEDULER_TaskIdentifier test_task;
142
143 /**
144  * Task called to shutdown test.
145  */
146 static GNUNET_SCHEDULER_TaskIdentifier shutdown_handle;
147
148 static char *topology_file;
149
150 static struct GNUNET_TESTING_Daemon *d1;
151
152 static GNUNET_PEER_Id pid1;
153
154 static struct GNUNET_TESTING_Daemon *d2;
155
156 static struct GNUNET_TESTING_Daemon *d3;
157
158 static struct GNUNET_MESH_Handle *h1;
159
160 static struct GNUNET_MESH_Handle *h2;
161
162 static struct GNUNET_MESH_Handle *h3;
163
164 static struct GNUNET_MESH_Tunnel *t;
165
166 static struct GNUNET_MESH_Tunnel *incoming_t;
167
168 static struct GNUNET_MESH_Tunnel *incoming_t2;
169
170 static struct GNUNET_TIME_Absolute start_time;
171
172 static struct GNUNET_TIME_Absolute end_time;
173
174 static struct GNUNET_TIME_Relative total_time;
175
176
177 /**
178  * Check whether peers successfully shut down.
179  */
180 static void
181 shutdown_callback (void *cls, const char *emsg)
182 {
183   if (emsg != NULL)
184   {
185 #if VERBOSE
186     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
187                 "Shutdown of peers failed!\n");
188 #endif
189     ok--;
190   }
191   else
192   {
193 #if VERBOSE
194     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
195                 "All peers successfully shut down!\n");
196 #endif
197   }
198   GNUNET_CONFIGURATION_destroy (testing_cfg);
199 }
200
201
202 /**
203  * Shut down peergroup, clean up.
204  */
205 static void
206 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
207 {
208 #if VERBOSE
209   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
210               "Ending test.\n");
211 #endif
212
213   if (disconnect_task != GNUNET_SCHEDULER_NO_TASK)
214   {
215     GNUNET_SCHEDULER_cancel (disconnect_task);
216     disconnect_task = GNUNET_SCHEDULER_NO_TASK;
217   }
218
219   if (NULL != h1)
220   {
221     GNUNET_MESH_disconnect (h1);
222     h1 = NULL;
223   }
224   if (NULL != h2)
225   {
226     GNUNET_MESH_disconnect (h2);
227     h2 = NULL;
228   }
229   if (test == MULTICAST && NULL != h3)
230   {
231     GNUNET_MESH_disconnect (h3);
232     h3 = NULL;
233   }
234   
235   if (data_file != NULL)
236     GNUNET_DISK_file_close (data_file);
237   GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
238 }
239
240
241 /**
242  * Disconnect from mesh services af all peers, call shutdown.
243  */
244 static void
245 disconnect_mesh_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
246 {
247   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
248               "disconnecting mesh service of peers\n");
249   disconnect_task = GNUNET_SCHEDULER_NO_TASK;
250   if (NULL != t)
251   {
252     GNUNET_MESH_tunnel_destroy(t);
253     t = NULL;
254   }
255   if (NULL != incoming_t)
256   {
257     GNUNET_MESH_tunnel_destroy(incoming_t);
258     incoming_t = NULL;
259   }
260   if (NULL != incoming_t2)
261   {
262     GNUNET_MESH_tunnel_destroy(incoming_t2);
263     incoming_t2 = NULL;
264   }
265   GNUNET_MESH_disconnect (h1);
266   GNUNET_MESH_disconnect (h2);
267   h1 = h2 = NULL;
268   if (test == MULTICAST)
269   {
270     GNUNET_MESH_disconnect (h3);
271     h3 = NULL;
272   }
273   if (GNUNET_SCHEDULER_NO_TASK != shutdown_handle)
274   {
275     GNUNET_SCHEDULER_cancel (shutdown_handle);
276     shutdown_handle = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
277   }
278 }
279
280 static size_t
281 tmt_rdy (void *cls, size_t size, void *buf);
282
283 static void
284 data_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
285 {
286   struct GNUNET_MESH_TransmitHandle *th;
287   if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0)
288     return;
289   th = GNUNET_MESH_notify_transmit_ready (t, GNUNET_NO,
290                                     GNUNET_TIME_UNIT_FOREVER_REL, &d2->id,
291                                     sizeof (struct GNUNET_MessageHeader),
292                                     &tmt_rdy, (void *) 1L);
293   if (NULL == th)
294   {
295     unsigned long i = (unsigned long) cls;
296
297     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Retransmission\n");
298     if (0 == i)
299     {
300       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "  in 1 ms\n");
301       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
302                                     &data_task, (void *)1UL);
303     }
304     else
305     {
306       i++;
307       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "in %u ms\n", i);
308       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply(
309                                       GNUNET_TIME_UNIT_MILLISECONDS,
310                                       i),
311                                     &data_task, (void *)i);
312     }
313   }
314 }
315
316 /**
317  * Transmit ready callback
318  *
319  * @param cls Closure.
320  * @param size Size of the buffer we have.
321  * @param buf Buffer to copy data to.
322  */
323 size_t
324 tmt_rdy (void *cls, size_t size, void *buf)
325 {
326   struct GNUNET_MessageHeader *msg = buf;
327
328   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
329               " tmt_rdy called\n");
330   if (size < sizeof (struct GNUNET_MessageHeader) || NULL == buf)
331     return 0;
332   msg->size = htons (sizeof (struct GNUNET_MessageHeader));
333   msg->type = htons ((long) cls);
334   if (test == SPEED)
335   {
336     data_sent++;
337     if (data_sent < 1000)
338     {
339       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
340               " Scheduling %d packet\n", data_sent);
341       GNUNET_SCHEDULER_add_now(&data_task, NULL);
342     }
343   }
344   return sizeof (struct GNUNET_MessageHeader);
345 }
346
347
348 /**
349  * Function is called whenever a message is received.
350  *
351  * @param cls closure (set from GNUNET_MESH_connect)
352  * @param tunnel connection to the other end
353  * @param tunnel_ctx place to store local state associated with the tunnel
354  * @param sender who sent the message
355  * @param message the actual message
356  * @param atsi performance data for the connection
357  * @return GNUNET_OK to keep the connection open,
358  *         GNUNET_SYSERR to close it (signal serious error)
359  */
360 int
361 data_callback (void *cls, struct GNUNET_MESH_Tunnel *tunnel, void **tunnel_ctx,
362                const struct GNUNET_PeerIdentity *sender,
363                const struct GNUNET_MessageHeader *message,
364                const struct GNUNET_ATS_Information *atsi)
365 {
366   long client = (long) cls;
367
368   switch (client)
369   {
370   case 1L:
371     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Origin client got a response!\n");
372     ok++;
373     GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
374     peers_responded++;
375     data_ack++;
376     if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
377     {
378       GNUNET_SCHEDULER_cancel (disconnect_task);
379       disconnect_task =
380           GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers,
381                                         NULL);
382     }
383     if (test == MULTICAST && peers_responded < 2)
384       return GNUNET_OK;
385     if (test == SPEED_ACK || test == SPEED)
386     {
387       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
388               " received ack %u\n", data_ack);
389       GNUNET_MESH_notify_transmit_ready (tunnel, GNUNET_NO,
390                                         GNUNET_TIME_UNIT_FOREVER_REL, sender,
391                                         sizeof (struct GNUNET_MessageHeader),
392                                         &tmt_rdy, (void *) 1L);
393       if (data_ack < 1000 && test != SPEED)
394         return GNUNET_OK;
395       end_time = GNUNET_TIME_absolute_get();
396       total_time = GNUNET_TIME_absolute_get_difference(start_time, end_time);
397       FPRINTF (stderr, "\nTest time %llu ms\n",
398                (unsigned long long) total_time.rel_value);
399       FPRINTF (stderr, "Test bandwidth: %f kb/s\n",
400                4 * 1000.0 / total_time.rel_value); // 4bytes * ms
401       FPRINTF (stderr, "Test throughput: %f packets/s\n\n",
402                1000.0 * 1000.0 / total_time.rel_value); // 1000 packets * ms
403       GAUGER ("MESH", "Tunnel 5 peers", 1000.0 * 1000.0 / total_time.rel_value,
404               "packets/s");
405     }
406     GNUNET_assert (tunnel == t);
407     GNUNET_MESH_tunnel_destroy (t);
408     t = NULL;
409     break;
410   case 2L:
411   case 3L:
412     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
413                 "Destination client %u got a message.\n",
414                 client);
415     ok++;
416     GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
417     if (SPEED != test || 1002 == ok)
418     {
419       GNUNET_MESH_notify_transmit_ready (tunnel, GNUNET_NO,
420                                         GNUNET_TIME_UNIT_FOREVER_REL, sender,
421                                         sizeof (struct GNUNET_MessageHeader),
422                                         &tmt_rdy, (void *) 1L);
423     }
424     else
425     {
426       data_received++;
427       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
428               " received data %u\n", data_received);
429       if (data_received < 1000)
430         return GNUNET_OK;
431     }
432     if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
433     {
434       GNUNET_SCHEDULER_cancel (disconnect_task);
435       disconnect_task =
436           GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers,
437                                         NULL);
438     }
439     break;
440   default:
441     break;
442   }
443   return GNUNET_OK;
444 }
445
446
447 /**
448  * Handlers, for diverse services
449  */
450 static struct GNUNET_MESH_MessageHandler handlers[] = {
451   {&data_callback, 1, sizeof (struct GNUNET_MessageHeader)},
452   {NULL, 0, 0}
453 };
454
455
456 /**
457  * Method called whenever another peer has added us to a tunnel
458  * the other peer initiated.
459  *
460  * @param cls closure
461  * @param tunnel new handle to the tunnel
462  * @param initiator peer that started the tunnel
463  * @param atsi performance information for the tunnel
464  * @return initial tunnel context for the tunnel
465  *         (can be NULL -- that's not an error)
466  */
467 static void *
468 incoming_tunnel (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
469                  const struct GNUNET_PeerIdentity *initiator,
470                  const struct GNUNET_ATS_Information *atsi)
471 {
472   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
473               "Incoming tunnel from %s to peer %d\n",
474               GNUNET_i2s (initiator), (long) cls);
475   ok++;
476   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
477   if ((long) cls == 2L)
478     incoming_t = tunnel;
479   else if ((long) cls == 3L)
480     incoming_t2 = tunnel;
481   else
482   {
483     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
484                 "Incoming tunnel for unknown client %lu\n", (long) cls);
485   }
486   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
487   {
488     GNUNET_SCHEDULER_cancel (disconnect_task);
489     disconnect_task =
490         GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers, NULL);
491   }
492   return NULL;
493 }
494
495 /**
496  * Function called whenever an inbound tunnel is destroyed.  Should clean up
497  * any associated state.
498  *
499  * @param cls closure (set from GNUNET_MESH_connect)
500  * @param tunnel connection to the other end (henceforth invalid)
501  * @param tunnel_ctx place where local state associated
502  *                   with the tunnel is stored
503  */
504 static void
505 tunnel_cleaner (void *cls, const struct GNUNET_MESH_Tunnel *tunnel,
506                 void *tunnel_ctx)
507 {
508   long i = (long) cls;
509
510   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
511               "Incoming tunnel disconnected at peer %d\n",
512               i);
513   if (2L == i)
514   {
515     ok++;
516     incoming_t = NULL;
517   }
518   else if (3L == i)
519   {
520     ok++;
521     incoming_t2 = NULL;
522   }
523   else
524     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
525                 "Unknown peer! %d\n", i);
526   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
527   peers_in_tunnel--;
528   if (peers_in_tunnel > 0)
529     return;
530
531   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
532   {
533     GNUNET_SCHEDULER_cancel (disconnect_task);
534     disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_mesh_peers, NULL);
535   }
536
537   return;
538 }
539
540
541 /**
542  * Method called whenever a tunnel falls apart.
543  *
544  * @param cls closure
545  * @param peer peer identity the tunnel stopped working with
546  */
547 static void
548 dh (void *cls, const struct GNUNET_PeerIdentity *peer)
549 {
550   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
551               "peer %s disconnected\n",
552               GNUNET_i2s (peer));
553   return;
554 }
555
556
557 /**
558  * Method called whenever a peer connects to a tunnel.
559  *
560  * @param cls closure
561  * @param peer peer identity the tunnel was created to, NULL on timeout
562  * @param atsi performance data for the connection
563  */
564 static void
565 ch (void *cls, const struct GNUNET_PeerIdentity *peer,
566     const struct GNUNET_ATS_Information *atsi)
567 {
568   struct GNUNET_PeerIdentity *dest;
569
570   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
571               "peer %s connected\n", GNUNET_i2s (peer));
572
573   if (0 == memcmp (&d2->id, peer, sizeof (d2->id)) && (long) cls == 1L)
574   {
575     ok++;
576   }
577   if (test == MULTICAST && 0 == memcmp (&d3->id, peer, sizeof (d3->id)) &&
578       (long) cls == 1L)
579   {
580     ok++;
581   }
582   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
583   switch (test)
584   {
585   case UNICAST:
586   case SPEED:
587   case SPEED_ACK:
588     dest = &d2->id;
589     break;
590   case MULTICAST:
591     peers_in_tunnel++;
592     if (peers_in_tunnel < 2)
593       return;
594     dest = NULL;
595     break;
596   default:
597     return;
598   }
599   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
600   {
601     GNUNET_SCHEDULER_cancel (disconnect_task);
602     disconnect_task =
603         GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers, NULL);
604     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
605                 "Sending data...\n");
606     peers_responded = 0;
607     data_ack = 0;
608     data_received = 0;
609     data_sent = 0;
610     start_time = GNUNET_TIME_absolute_get();
611     GNUNET_MESH_notify_transmit_ready (t, GNUNET_NO,
612                                        GNUNET_TIME_UNIT_FOREVER_REL, dest,
613                                        sizeof (struct GNUNET_MessageHeader),
614                                        &tmt_rdy, (void *) 1L);
615   }
616   else
617   {
618     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
619                 "Disconnect already run?\n");
620     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
621                 "Aborting...\n");
622   }
623   return;
624 }
625
626
627 static void
628 do_test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
629 {
630   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test_task\n");
631   if (test == MULTICAST)
632   {
633     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
634                 "add peer 3\n");
635     GNUNET_MESH_peer_request_connect_add (t, &d3->id);
636   }
637   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "add peer 2\n");
638   GNUNET_MESH_peer_request_connect_add (t, &d2->id);
639   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
640               "schedule timeout in SHORT_TIME\n");
641   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
642   {
643     GNUNET_SCHEDULER_cancel (disconnect_task);
644     disconnect_task =
645         GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers, NULL);
646   }
647 }
648
649
650 /**
651  * connect_mesh_service: connect to the mesh service of one of the peers
652  *
653  */
654 static void
655 connect_mesh_service (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
656 {
657   GNUNET_MESH_ApplicationType app;
658
659   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
660               "connect_mesh_service\n");
661
662   d2 = GNUNET_TESTING_daemon_get (pg, 4);
663   if (test == MULTICAST)
664   {
665     d3 = GNUNET_TESTING_daemon_get (pg, 3);
666   }
667   app = (GNUNET_MESH_ApplicationType) 0;
668
669 #if VERBOSE
670   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
671               "connecting to mesh service of peer %s\n",
672               GNUNET_i2s (&d1->id));
673   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
674               "connecting to mesh service of peer %s\n",
675               GNUNET_i2s (&d2->id));
676   if (test == MULTICAST)
677   {
678     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
679                 "connecting to mesh service of peer %s\n",
680                 GNUNET_i2s (&d3->id));
681   }
682 #endif
683   h1 = GNUNET_MESH_connect (d1->cfg, (void *) 1L, NULL, &tunnel_cleaner,
684                             handlers, &app);
685   h2 = GNUNET_MESH_connect (d2->cfg, (void *) 2L, &incoming_tunnel,
686                             &tunnel_cleaner, handlers, &app);
687   if (test == MULTICAST)
688   {
689     h3 = GNUNET_MESH_connect (d3->cfg, (void *) 3L, &incoming_tunnel,
690                               &tunnel_cleaner, handlers, &app);
691   }
692   t = GNUNET_MESH_tunnel_create (h1, NULL, &ch, &dh, (void *) 1L);
693   if (SPEED_MIN == test)
694   {
695     GNUNET_MESH_tunnel_speed_min(t);
696     test = SPEED;
697   }
698   if (SPEED_NOBUF == test)
699   {
700     GNUNET_MESH_tunnel_buffer(t, GNUNET_NO);
701     test = SPEED;
702   }
703   peers_in_tunnel = 0;
704   test_task =
705       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
706                                     (GNUNET_TIME_UNIT_SECONDS, 1), &do_test,
707                                     NULL);
708 }
709
710
711
712 /**
713  * peergroup_ready: start test when all peers are connected
714  * @param cls closure
715  * @param emsg error message
716  */
717 static void
718 peergroup_ready (void *cls, const char *emsg)
719 {
720   char *buf;
721   int buf_len;
722   unsigned int i;
723
724   if (emsg != NULL)
725   {
726     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
727                 "Peergroup callback called with error, aborting test!\n");
728     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
729                 "Error from testing: `%s'\n", emsg);
730     ok--;
731     GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
732     return;
733   }
734 #if VERBOSE
735   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
736               "************************************************************\n");
737   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
738               "Peer Group started successfully!\n");
739   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
740               "Have %u connections\n",
741               total_connections);
742 #endif
743
744   if (data_file != NULL)
745   {
746     buf = NULL;
747     buf_len = GNUNET_asprintf (&buf, "CONNECTIONS_0: %u\n", total_connections);
748     if (buf_len > 0)
749       GNUNET_DISK_file_write (data_file, buf, buf_len);
750     GNUNET_free (buf);
751   }
752   peers_running = GNUNET_TESTING_daemons_running (pg);
753   for (i = 0; i < num_peers; i++)
754   {
755     GNUNET_PEER_Id peer_id;
756
757     d1 = GNUNET_TESTING_daemon_get (pg, i);
758     peer_id = GNUNET_PEER_intern (&d1->id);
759     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  %u: %s\n",
760                 peer_id, GNUNET_i2s (&d1->id));
761   }
762   d1 = GNUNET_TESTING_daemon_get (pg, 0);
763   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
764               "Peer looking: %s\n",
765               GNUNET_i2s (&d1->id));
766   pid1 = GNUNET_PEER_intern (&d1->id);
767
768   GNUNET_SCHEDULER_add_now (&connect_mesh_service, NULL);
769   disconnect_task =
770       GNUNET_SCHEDULER_add_delayed (wait_time, &disconnect_mesh_peers, NULL);
771
772 }
773
774
775 /**
776  * Function that will be called whenever two daemons are connected by
777  * the testing library.
778  *
779  * @param cls closure
780  * @param first peer id for first daemon
781  * @param second peer id for the second daemon
782  * @param distance distance between the connected peers
783  * @param first_cfg config for the first daemon
784  * @param second_cfg config for the second daemon
785  * @param first_daemon handle for the first daemon
786  * @param second_daemon handle for the second daemon
787  * @param emsg error message (NULL on success)
788  */
789 static void
790 connect_cb (void *cls, const struct GNUNET_PeerIdentity *first,
791             const struct GNUNET_PeerIdentity *second, uint32_t distance,
792             const struct GNUNET_CONFIGURATION_Handle *first_cfg,
793             const struct GNUNET_CONFIGURATION_Handle *second_cfg,
794             struct GNUNET_TESTING_Daemon *first_daemon,
795             struct GNUNET_TESTING_Daemon *second_daemon, const char *emsg)
796 {
797   if (emsg == NULL)
798   {
799     total_connections++;
800   }
801   else
802   {
803     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
804                 "Problem with new connection (%s)\n",
805                 emsg);
806     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " (%s)\n", GNUNET_i2s (first));
807     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " (%s)\n", GNUNET_i2s (second));
808   }
809
810 }
811
812
813 /**
814  * run: load configuration options and schedule test to run (start peergroup)
815  * @param cls closure
816  * @param args argv
817  * @param cfgfile configuration file name (can be NULL)
818  * @param cfg configuration handle
819  */
820 static void
821 run (void *cls, char *const *args, const char *cfgfile,
822      const struct GNUNET_CONFIGURATION_Handle *cfg)
823 {
824   char *temp_str;
825   struct GNUNET_TESTING_Host *hosts;
826   char *data_filename;
827
828   ok = 0;
829   testing_cfg = GNUNET_CONFIGURATION_dup (cfg);
830
831   GNUNET_log_setup ("test_mesh_small",
832 #if VERBOSE
833                     "DEBUG",
834 #else
835                     "WARNING",
836 #endif
837                     NULL);
838
839 #if VERBOSE
840   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
841               "Starting daemons.\n");
842   GNUNET_CONFIGURATION_set_value_string (testing_cfg, "testing_old",
843                                          "use_progressbars", "YES");
844 #endif
845
846   if (GNUNET_OK !=
847       GNUNET_CONFIGURATION_get_value_number (testing_cfg, "testing_old",
848                                              "num_peers", &num_peers))
849   {
850     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
851                 "Option TESTING:NUM_PEERS is required!\n");
852     return;
853   }
854
855   if (GNUNET_OK !=
856       GNUNET_CONFIGURATION_get_value_time (testing_cfg, "test_mesh_small",
857                                            "WAIT_TIME", &wait_time))
858   {
859     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
860                 "Option test_mesh_small:wait_time is required!\n");
861     return;
862   }
863
864   if (GNUNET_OK !=
865       GNUNET_CONFIGURATION_get_value_string (testing_cfg, "testing_old",
866                                              "topology_output_file",
867                                              &topology_file))
868   {
869     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
870                 "Option test_mesh_small:topology_output_file is required!\n");
871     return;
872   }
873
874   if (GNUNET_OK ==
875       GNUNET_CONFIGURATION_get_value_string (testing_cfg, "test_mesh_small",
876                                              "data_output_file",
877                                              &data_filename))
878   {
879     data_file =
880         GNUNET_DISK_file_open (data_filename,
881                                GNUNET_DISK_OPEN_READWRITE |
882                                GNUNET_DISK_OPEN_CREATE,
883                                GNUNET_DISK_PERM_USER_READ |
884                                GNUNET_DISK_PERM_USER_WRITE);
885     if (data_file == NULL)
886     {
887       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to open %s for output!\n",
888                   data_filename);
889       GNUNET_free (data_filename);
890     }
891   }
892
893   if (GNUNET_YES ==
894       GNUNET_CONFIGURATION_get_value_string (cfg, "test_mesh_small",
895                                              "output_file", &temp_str))
896   {
897     output_file =
898         GNUNET_DISK_file_open (temp_str,
899                                GNUNET_DISK_OPEN_READWRITE |
900                                GNUNET_DISK_OPEN_CREATE,
901                                GNUNET_DISK_PERM_USER_READ |
902                                GNUNET_DISK_PERM_USER_WRITE);
903     if (output_file == NULL)
904       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to open %s for output!\n",
905                   temp_str);
906   }
907   GNUNET_free_non_null (temp_str);
908
909   hosts = GNUNET_TESTING_hosts_load (testing_cfg);
910
911   pg = GNUNET_TESTING_peergroup_start (testing_cfg, num_peers, TIMEOUT,
912                                        &connect_cb, &peergroup_ready, NULL,
913                                        hosts);
914   GNUNET_assert (pg != NULL);
915   shutdown_handle =
916     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
917                                     &shutdown_task, NULL);
918 }
919
920
921
922 /**
923  * test_mesh_small command line options
924  */
925 static struct GNUNET_GETOPT_CommandLineOption options[] = {
926   {'V', "verbose", NULL,
927    gettext_noop ("be verbose (print progress information)"),
928    0, &GNUNET_GETOPT_set_one, &verbose},
929   GNUNET_GETOPT_OPTION_END
930 };
931
932
933 /**
934  * Main: start test
935  */
936 int
937 main (int argc, char *argv[])
938 {
939   char * argv2[] = {
940     argv[0],
941     "-c",
942     "test_mesh_small.conf",
943 #if VERBOSE
944     "-L",
945     "DEBUG",
946 #endif
947     NULL
948   };
949   int argc2 = (sizeof (argv2) / sizeof (char *)) - 1;
950
951   /* Each peer is supposed to generate the following callbacks:
952    * 1 incoming tunnel (@dest)
953    * 1 connected peer (@orig)
954    * 1 received data packet (@dest)
955    * 1 received data packet (@orig)
956    * 1 received tunnel destroy (@dest)
957    * _________________________________
958    * 5 x ok expected per peer
959    */
960   int ok_goal;
961
962   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Start\n");
963   if (strstr (argv[0], "test_mesh_small_unicast") != NULL)
964   {
965     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "UNICAST\n");
966     test = UNICAST;
967     ok_goal = 5;
968   }
969   else if (strstr (argv[0], "test_mesh_small_multicast") != NULL)
970   {
971     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MULTICAST\n");
972     test = MULTICAST;
973     ok_goal = 10;
974   }
975   else if (strstr (argv[0], "test_mesh_small_speed_ack") != NULL)
976   {
977    /* Each peer is supposed to generate the following callbacks:
978     * 1 incoming tunnel (@dest)
979     * 1 connected peer (@orig)
980     * 1000 received data packet (@dest)
981     * 1000 received data packet (@orig)
982     * 1 received tunnel destroy (@dest)
983     * _________________________________
984     * 5 x ok expected per peer
985     */
986     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SPEED_ACK\n");
987     test = SPEED_ACK;
988     ok_goal = 2003;
989     argv2 [3] = NULL; // remove -L DEBUG
990 #if VERBOSE
991     argc2 -= 2;
992 #endif
993   }
994   else if (strstr (argv[0], "test_mesh_small_speed") != NULL)
995   {
996    /* Each peer is supposed to generate the following callbacks:
997     * 1 incoming tunnel (@dest)
998     * 1 connected peer (@orig)
999     * 1000 received data packet (@dest)
1000     * 1received data packet (@orig)
1001     * 1 received tunnel destroy (@dest)
1002     * _________________________________
1003     */
1004     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SPEED\n");
1005     ok_goal = 1004;
1006     if (strstr (argv[0], "_min") != NULL)
1007       test = SPEED_MIN;
1008     else if (strstr (argv[0], "_nobuf") != NULL)
1009       test = SPEED_NOBUF;
1010     else
1011       test = SPEED;
1012   }
1013   else
1014   {
1015     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "UNKNOWN\n");
1016     test = SETUP;
1017     ok_goal = 0;
1018   }
1019
1020   GNUNET_PROGRAM_run (argc2, argv2,
1021                       "test_mesh_small",
1022                       gettext_noop ("Test mesh in a small network."), options,
1023                       &run, NULL);
1024 #if REMOVE_DIR
1025   GNUNET_DISK_directory_remove ("/tmp/test_mesh_small");
1026 #endif
1027   if (ok_goal > ok)
1028   {
1029     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1030                 "FAILED! (%d/%d)\n", ok, ok_goal);
1031     return 1;
1032   }
1033   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "success\n");
1034   return 0;
1035 }
1036
1037 /* end of test_mesh_small.c */