clique topology optimization, progress meter for connecting
[oweals/gnunet.git] / src / testing / test_testing_large_topology.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009 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 testing/test_testing_topology.c
22  * @brief base testcase for testing all the topologies provided
23  */
24 #include "platform.h"
25 #include "gnunet_testing_lib.h"
26 #include "gnunet_core_service.h"
27 #include "gnunet_os_lib.h"
28
29 #define VERBOSE GNUNET_YES
30
31 #define DELAY_FOR_LOGGING GNUNET_NO
32
33 #define SECONDS_PER_PEER_START 120
34
35 #define DEFAULT_NUM_PEERS 4
36
37 #define MAX_OUTSTANDING_CONNECTIONS 100
38
39 static float fail_percentage = 0.05;
40
41 static int ok;
42
43 struct GNUNET_TIME_Relative connect_timeout;
44
45 static unsigned long long connect_attempts;
46
47 static unsigned long long num_peers;
48
49 static unsigned int topology_connections;
50
51 static unsigned int total_connections;
52
53 static unsigned int failed_connections;
54
55 static unsigned int total_server_connections;
56
57 static unsigned int total_messages_received;
58
59 static unsigned int expected_messages;
60
61 static unsigned int expected_connections;
62
63 static unsigned long long peers_left;
64
65 static struct GNUNET_TESTING_PeerGroup *pg;
66
67 const struct GNUNET_CONFIGURATION_Handle *main_cfg;
68
69 GNUNET_SCHEDULER_TaskIdentifier die_task;
70
71 static char *dotOutFileName;
72
73 static struct GNUNET_TIME_Relative settle_time;
74
75 static FILE *dotOutFile;
76
77 static char *topology_string;
78
79 static char *blacklist_transports;
80
81 static int transmit_ready_scheduled;
82
83 static int transmit_ready_failed;
84
85 static int transmit_ready_called;
86
87 struct GNUNET_TIME_Relative test_timeout;
88
89 struct GNUNET_TIME_Relative timeout;
90
91 static unsigned int modnum;
92
93 static unsigned int dotnum;
94
95 static enum GNUNET_TESTING_Topology topology;
96
97 static enum GNUNET_TESTING_Topology blacklist_topology = GNUNET_TESTING_TOPOLOGY_NONE;  /* Don't do any blacklisting */
98
99 static enum GNUNET_TESTING_Topology connection_topology = GNUNET_TESTING_TOPOLOGY_NONE; /* NONE actually means connect all allowed peers */
100
101 static enum GNUNET_TESTING_TopologyOption connect_topology_option =
102   GNUNET_TESTING_TOPOLOGY_OPTION_ALL;
103
104 static double connect_topology_option_modifier = 0.0;
105
106 static char *test_directory;
107
108 #define MTYPE 12345
109
110 struct GNUNET_TestMessage
111 {
112   /**
113    * Header of the message
114    */
115   struct GNUNET_MessageHeader header;
116
117   /**
118    * Unique identifier for this message.
119    */
120   uint32_t uid;
121 };
122
123 struct TestMessageContext
124 {
125   /* This is a linked list */
126   struct TestMessageContext *next;
127
128   /* Handle to the sending peer core */
129   struct GNUNET_CORE_Handle *peer1handle;
130
131   /* Handle to the receiving peer core */
132   struct GNUNET_CORE_Handle *peer2handle;
133
134   /* Handle to the sending peer daemon */
135   struct GNUNET_TESTING_Daemon *peer1;
136
137   /* Handle to the receiving peer daemon */
138   struct GNUNET_TESTING_Daemon *peer2;
139
140   /* Identifier for this message, so we don't disconnect other peers! */
141   uint32_t uid;
142
143   /* Has peer1 been notified already of a connection to peer2? */
144   int peer1notified;
145
146   /* Has the core of peer2 been connected already? */
147   int peer2connected;
148
149   /* Task for disconnecting cores, allow task to be cancelled on shutdown */
150   GNUNET_SCHEDULER_TaskIdentifier disconnect_task;
151
152 };
153
154 static struct TestMessageContext *test_messages;
155
156 /**
157  * Check whether peers successfully shut down.
158  */
159 void
160 shutdown_callback (void *cls, const char *emsg)
161 {
162   if (emsg != NULL)
163     {
164 #if VERBOSE
165       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutdown of peers failed!\n");
166 #endif
167       if (ok == 0)
168         ok = 666;
169     }
170   else
171     {
172 #if VERBOSE
173       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
174                   "All peers successfully shut down!\n");
175 #endif
176     }
177 }
178
179 #if DELAY_FOR_LOGGING
180 static void
181 gather_log_data ()
182 {
183   char *peer_number;
184   char *connect_number;
185   struct GNUNET_OS_Process *mem_process;
186   GNUNET_asprintf (&peer_number, "%llu", num_peers);
187   GNUNET_asprintf (&connect_number, "%llu", expected_connections);
188   mem_process = GNUNET_OS_start_process (NULL, NULL, "./memsize.pl",
189                                          "memsize.pl", "totals.txt",
190                                          peer_number, connect_number, NULL);
191   GNUNET_OS_process_wait (mem_process);
192   GNUNET_OS_process_close (mem_process);
193   mem_process = NULL;
194 }
195
196 #endif
197
198 static void
199 finish_testing ()
200 {
201   GNUNET_assert (pg != NULL);
202   struct TestMessageContext *pos;
203   struct TestMessageContext *free_pos;
204 #if VERBOSE
205   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
206               "Called finish testing, stopping daemons.\n");
207 #endif
208
209   pos = test_messages;
210   while (pos != NULL)
211     {
212       if (pos->peer1handle != NULL)
213         {
214           GNUNET_CORE_disconnect (pos->peer1handle);
215           pos->peer1handle = NULL;
216         }
217       if (pos->peer2handle != NULL)
218         {
219           GNUNET_CORE_disconnect (pos->peer2handle);
220           pos->peer2handle = NULL;
221         }
222       free_pos = pos;
223       pos = pos->next;
224       if (free_pos->disconnect_task != GNUNET_SCHEDULER_NO_TASK)
225         {
226           GNUNET_SCHEDULER_cancel (free_pos->disconnect_task);
227         }
228       GNUNET_free (free_pos);
229     }
230 #if VERBOSE
231   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
232               "Transmit_ready's scheduled %d, failed %d, transmit_ready's called %d\n",
233               transmit_ready_scheduled, transmit_ready_failed,
234               transmit_ready_called);
235 #endif
236
237 #if VERBOSE
238   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Calling daemons_stop\n");
239 #endif
240   GNUNET_TESTING_daemons_stop (pg, timeout, &shutdown_callback, NULL);
241
242   if (dotOutFile != NULL)
243     {
244       fprintf (dotOutFile, "}");
245       fclose (dotOutFile);
246     }
247
248   ok = 0;
249 }
250
251
252 static void
253 disconnect_cores (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
254 {
255   struct TestMessageContext *pos = cls;
256
257   /* Disconnect from the respective cores */
258 #if VERBOSE > 1
259   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
260               "Disconnecting from peer 1 `%4s'\n",
261               GNUNET_i2s (&pos->peer1->id));
262 #endif
263   if (pos->peer1handle != NULL)
264     GNUNET_CORE_disconnect (pos->peer1handle);
265 #if VERBOSE > 1
266   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
267               "Disconnecting from peer 2 `%4s'\n",
268               GNUNET_i2s (&pos->peer2->id));
269 #endif
270   if (pos->peer2handle != NULL)
271     GNUNET_CORE_disconnect (pos->peer2handle);
272   /* Set handles to NULL so test case can be ended properly */
273   pos->peer1handle = NULL;
274   pos->peer2handle = NULL;
275   pos->disconnect_task = GNUNET_SCHEDULER_NO_TASK;
276   /* Decrement total connections so new can be established */
277   total_server_connections -= 2;
278 }
279
280
281 static void
282 topology_cb (void *cls,
283              const struct GNUNET_PeerIdentity *first,
284              const struct GNUNET_PeerIdentity *second, const char *emsg)
285 {
286   FILE *outfile = cls;
287   if (first != NULL)
288     {
289       if (outfile != NULL)
290         {
291           fprintf (outfile, "\t\"%s\" -- ", GNUNET_i2s (first));
292           fprintf (outfile, "\"%s\";\n", GNUNET_i2s (second));
293         }
294       topology_connections++;
295     }
296   else
297     {
298       fprintf (stderr,
299                "Finished iterating over topology, %d total connections!\n",
300                topology_connections);
301       if (outfile != NULL)
302         {
303           fprintf (outfile, "}\n");
304           fclose (outfile);
305           GNUNET_SCHEDULER_add_now (&finish_testing, NULL);
306         }
307     }
308 }
309
310 static int
311 process_mtype (void *cls,
312                const struct GNUNET_PeerIdentity *peer,
313                const struct GNUNET_MessageHeader *message,
314                const struct GNUNET_TRANSPORT_ATS_Information *atsi)
315 {
316   char *dotOutFileNameFinished;
317   FILE *dotOutFileFinished;
318   struct TestMessageContext *pos = cls;
319   struct GNUNET_TestMessage *msg = (struct GNUNET_TestMessage *) message;
320   if (pos->uid != ntohl (msg->uid))
321     return GNUNET_OK;
322
323 #if VERBOSE
324   if ((total_messages_received) % modnum == 0)
325     {
326       if (total_messages_received == 0)
327         fprintf (stdout, "0%%");
328       else
329         fprintf (stdout, "%d%%",
330                  (int) (((float) total_messages_received /
331                          expected_messages) * 100));
332
333     }
334   else if (total_messages_received % dotnum == 0)
335     {
336       fprintf (stdout, ".");
337     }
338   fflush (stdout);
339 #endif
340
341   total_messages_received++;
342
343 #if VERBOSE > 1
344   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
345               "Received message from `%4s', type %d.\n", GNUNET_i2s (peer),
346               ntohs (message->type));
347   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
348               "Total messages received %d, expected %d.\n",
349               total_messages_received, expected_messages);
350 #endif
351
352   if (total_messages_received == expected_messages)
353     {
354 #if VERBOSE
355       fprintf (stdout, "100%%]\n");
356 #endif
357       GNUNET_SCHEDULER_cancel (die_task);
358       GNUNET_asprintf (&dotOutFileNameFinished, "%s.dot", "final_topology");
359       dotOutFileFinished = fopen (dotOutFileNameFinished, "w");
360       GNUNET_free (dotOutFileNameFinished);
361       if (dotOutFileFinished != NULL)
362         {
363           fprintf (dotOutFileFinished, "strict graph G {\n");
364         }
365       topology_connections = 0;
366       GNUNET_TESTING_get_topology (pg, &topology_cb, dotOutFileFinished);
367       //GNUNET_SCHEDULER_add_now (&finish_testing, NULL);
368     }
369   else
370     {
371       pos->disconnect_task =
372         GNUNET_SCHEDULER_add_now (&disconnect_cores, pos);
373     }
374
375   return GNUNET_OK;
376 }
377
378 static void
379 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
380 {
381   char *msg = cls;
382   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
383               "End badly was called (%s)... stopping daemons.\n", msg);
384   struct TestMessageContext *pos;
385   struct TestMessageContext *free_pos;
386
387   pos = test_messages;
388   while (pos != NULL)
389     {
390       if (pos->peer1handle != NULL)
391         {
392           GNUNET_CORE_disconnect (pos->peer1handle);
393           pos->peer1handle = NULL;
394         }
395       if (pos->peer2handle != NULL)
396         {
397           GNUNET_CORE_disconnect (pos->peer2handle);
398           pos->peer2handle = NULL;
399         }
400       free_pos = pos;
401       pos = pos->next;
402       GNUNET_free (free_pos);
403     }
404
405 #if VERBOSE
406   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
407               "Transmit_ready's scheduled %d, failed %d, transmit_ready's called %d\n",
408               transmit_ready_scheduled, transmit_ready_failed,
409               transmit_ready_called);
410   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
411               "Total messages received %d, expected %d.\n",
412               total_messages_received, expected_messages);
413 #endif
414
415   if (pg != NULL)
416     {
417       GNUNET_TESTING_daemons_stop (pg, timeout, &shutdown_callback, NULL);
418       ok = 7331;                /* Opposite of leet */
419     }
420   else
421     ok = 401;                   /* Never got peers started */
422
423   if (dotOutFile != NULL)
424     {
425       fprintf (dotOutFile, "}");
426       fclose (dotOutFile);
427     }
428 }
429
430 static size_t
431 transmit_ready (void *cls, size_t size, void *buf)
432 {
433   struct GNUNET_TestMessage *m;
434   struct TestMessageContext *pos = cls;
435
436   GNUNET_assert (buf != NULL);
437   m = (struct GNUNET_TestMessage *) buf;
438   m->header.type = htons (MTYPE);
439   m->header.size = htons (sizeof (struct GNUNET_TestMessage));
440   m->uid = htonl (pos->uid);
441   transmit_ready_called++;
442 #if VERBOSE > 1
443   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
444               "transmit ready for peer %s\ntransmit_ready's scheduled %d, transmit_ready's called %d\n",
445               GNUNET_i2s (&pos->peer1->id), transmit_ready_scheduled,
446               transmit_ready_called);
447 #endif
448   return sizeof (struct GNUNET_TestMessage);
449 }
450
451
452 static struct GNUNET_CORE_MessageHandler no_handlers[] = {
453   {NULL, 0, 0}
454 };
455
456 static struct GNUNET_CORE_MessageHandler handlers[] = {
457   {&process_mtype, MTYPE, sizeof (struct GNUNET_TestMessage)},
458   {NULL, 0, 0}
459 };
460
461 static void
462 init_notify_peer2 (void *cls,
463                    struct GNUNET_CORE_Handle *server,
464                    const struct GNUNET_PeerIdentity *my_identity,
465                    const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
466                    *publicKey)
467 {
468   struct TestMessageContext *pos = cls;
469
470   total_server_connections++;
471
472   pos->peer2connected = GNUNET_YES;
473   if (pos->peer1notified == GNUNET_YES) /* Peer 1 has been notified of connection to peer 2 */
474     {
475 #if VERBOSE > 1
476       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
477                   "Scheduling message send to peer `%s' from peer `%s' (init_notify_peer2)\n",
478                   GNUNET_i2s (my_identity), GNUNET_h2s(&pos->peer1->id.hashPubKey));
479 #endif
480       if (NULL == GNUNET_CORE_notify_transmit_ready (pos->peer1handle,
481                                                      0,
482                                                      timeout,
483                                                      &pos->peer2->id,
484                                                      sizeof (struct
485                                                              GNUNET_TestMessage),
486                                                      &transmit_ready, pos))
487         {
488           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
489                       "RECEIVED NULL when asking core (1) for transmission to peer `%4s'\n",
490                       GNUNET_i2s (&pos->peer2->id));
491           transmit_ready_failed++;
492         }
493       else
494         {
495           transmit_ready_scheduled++;
496         }
497     }
498 }
499
500 /**
501  * Method called whenever a given peer connects.
502  *
503  * @param cls closure
504  * @param peer peer identity this notification is about
505  * @param atsi performance data for the connection
506  */
507 static void connect_notify_peers (void *cls,
508                                   const struct
509                                   GNUNET_PeerIdentity *peer,
510                                   const struct GNUNET_TRANSPORT_ATS_Information *atsi)
511 {
512   struct TestMessageContext *pos = cls;
513
514   if (0 == memcmp(peer, &pos->peer2->id, sizeof(struct GNUNET_PeerIdentity)))
515     {
516       pos->peer1notified = GNUNET_YES;
517 #if VERBOSE > 1
518       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
519                 "Peer `%s' notified of connection to peer `%s'\n",
520                 GNUNET_i2s (&pos->peer1->id), GNUNET_h2s(&peer->hashPubKey));
521 #endif
522     }
523   else
524     return;
525
526   if (pos->peer2connected == GNUNET_YES) /* Already connected and notified of connection, send message! */
527     {
528 #if VERBOSE > 1
529       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
530                 "Scheduling message send to peer `%s' from peer `%s' (init_notify_peer2)\n",
531                 GNUNET_i2s (&pos->peer2->id), GNUNET_h2s(&pos->peer1->id.hashPubKey));
532 #endif
533       if (NULL == GNUNET_CORE_notify_transmit_ready (pos->peer1handle,
534                                                      0,
535                                                      timeout,
536                                                      &pos->peer2->id,
537                                                      sizeof (struct
538                                                              GNUNET_TestMessage),
539                                                      &transmit_ready, pos))
540         {
541           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
542                       "RECEIVED NULL when asking core (1) for transmission to peer `%4s'\n",
543                       GNUNET_i2s (&pos->peer2->id));
544           transmit_ready_failed++;
545         }
546       else
547         {
548           transmit_ready_scheduled++;
549         }
550     }
551 }
552
553 static void
554 init_notify_peer1 (void *cls,
555                    struct GNUNET_CORE_Handle *server,
556                    const struct GNUNET_PeerIdentity *my_identity,
557                    const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
558                    *publicKey)
559 {
560   struct TestMessageContext *pos = cls;
561   total_server_connections++;
562
563 #if VERBOSE > 1
564   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
565               "Core connection to `%4s' established, setting up handles\n",
566               GNUNET_i2s (my_identity));
567 #endif
568
569   /*
570    * Connect to the receiving peer
571    */
572   pos->peer2handle = GNUNET_CORE_connect (pos->peer2->cfg,
573                                           1,
574                                           pos,
575                                           &init_notify_peer2,
576                                           NULL,
577                                           NULL,
578                                           NULL, NULL,
579                                           GNUNET_YES, NULL, GNUNET_YES,
580                                           handlers);
581
582 }
583
584
585 static void
586 send_test_messages (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
587 {
588   struct TestMessageContext *pos = cls;
589
590   if ((pos == test_messages) && (settle_time.rel_value > 0))
591     {
592       topology_connections = 0;
593       GNUNET_TESTING_get_topology (pg, &topology_cb, NULL);
594     }
595   if ((tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN) || (cls == NULL))
596     return;
597
598   if (die_task == GNUNET_SCHEDULER_NO_TASK)
599     {
600       die_task = GNUNET_SCHEDULER_add_delayed (test_timeout,
601                                                &end_badly,
602                                                "from send test messages (timeout)");
603     }
604
605   if (total_server_connections >= MAX_OUTSTANDING_CONNECTIONS)
606     {
607       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
608                                     (GNUNET_TIME_UNIT_SECONDS, 1),
609                                     &send_test_messages, pos);
610       return;                   /* Otherwise we'll double schedule messages here! */
611     }
612
613   /*
614    * Connect to the sending peer
615    */
616   pos->peer1handle = GNUNET_CORE_connect (pos->peer1->cfg,
617                                           1,
618                                           pos,
619                                           &init_notify_peer1,
620                                           &connect_notify_peers, NULL,
621                                           NULL,
622                                           NULL,
623                                           GNUNET_NO, NULL, GNUNET_NO,
624                                           no_handlers);
625
626   GNUNET_assert (pos->peer1handle != NULL);
627
628   if (total_server_connections < MAX_OUTSTANDING_CONNECTIONS)
629     {
630       GNUNET_SCHEDULER_add_now (&send_test_messages, pos->next);
631     }
632   else
633     {
634       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
635                                     (GNUNET_TIME_UNIT_SECONDS, 1),
636                                     &send_test_messages, pos->next);
637     }
638 }
639
640
641 void
642 topology_callback (void *cls,
643                    const struct GNUNET_PeerIdentity *first,
644                    const struct GNUNET_PeerIdentity *second,
645                    uint32_t distance,
646                    const struct GNUNET_CONFIGURATION_Handle *first_cfg,
647                    const struct GNUNET_CONFIGURATION_Handle *second_cfg,
648                    struct GNUNET_TESTING_Daemon *first_daemon,
649                    struct GNUNET_TESTING_Daemon *second_daemon,
650                    const char *emsg)
651 {
652   struct TestMessageContext *temp_context;
653   if (emsg == NULL)
654     {
655 #if VERBOSE
656       if ((total_connections) % modnum == 0)
657         {
658           if (total_connections == 0)
659             fprintf (stdout, "0%%");
660           else
661             fprintf (stdout, "%d%%",
662                      (int) (((float) total_connections /
663                              expected_connections) * 100));
664
665         }
666       else if (total_connections % dotnum == 0)
667         {
668           fprintf (stdout, ".");
669         }
670       fflush (stdout);
671 #endif
672       total_connections++;
673 #if VERBOSE > 1
674       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "connected peer %s to peer %s\n",
675                   first_daemon->shortname, second_daemon->shortname);
676 #endif
677       temp_context = GNUNET_malloc (sizeof (struct TestMessageContext));
678       temp_context->peer1 = first_daemon;
679       temp_context->peer2 = second_daemon;
680       temp_context->next = test_messages;
681       temp_context->uid = total_connections;
682       temp_context->disconnect_task = GNUNET_SCHEDULER_NO_TASK;
683       test_messages = temp_context;
684
685       expected_messages++;
686       if (dotOutFile != NULL)
687         fprintf (dotOutFile, "\tn%s -- n%s;\n", first_daemon->shortname,
688                  second_daemon->shortname);
689     }
690 #if VERBOSE
691   else
692     {
693       failed_connections++;
694       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
695                   "Failed to connect peer %s to peer %s with error :\n%s\n",
696                   first_daemon->shortname, second_daemon->shortname, emsg);
697     }
698 #endif
699
700   if (total_connections == expected_connections)
701     {
702 #if VERBOSE
703       fprintf (stdout, "100%%]\n");
704 #endif
705 #if VERBOSE
706       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
707                   "Created %d total connections, which is our target number!  Calling send messages.\n",
708                   total_connections);
709 #endif
710       modnum = expected_messages / 4;
711       dotnum = (expected_messages / 50) + 1;
712       GNUNET_SCHEDULER_cancel (die_task);
713       die_task = GNUNET_SCHEDULER_NO_TASK;
714 #if DELAY_FOR_LOGGING
715       fprintf (stdout, "Sending test messages in 10 seconds.\n");
716       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
717                                     (GNUNET_TIME_UNIT_SECONDS, 10),
718                                     &send_test_messages, test_messages);
719       gather_log_data ();
720 #else
721       if (settle_time.rel_value > 0)
722         {
723           GNUNET_TESTING_get_topology (pg, &topology_cb, NULL);
724         }
725       GNUNET_SCHEDULER_add_delayed (settle_time, &send_test_messages,
726                                     test_messages);
727 #endif
728 #if VERBOSE
729       fprintf (stdout, "Test message progress: [");
730 #endif
731
732     }
733   else if (total_connections + failed_connections == expected_connections)
734     {
735       if (failed_connections <
736           (unsigned int) (fail_percentage * total_connections))
737         {
738           GNUNET_SCHEDULER_cancel (die_task);
739           die_task = GNUNET_SCHEDULER_NO_TASK;
740           GNUNET_SCHEDULER_add_now (&send_test_messages, test_messages);
741         }
742       else
743         {
744           GNUNET_SCHEDULER_cancel (die_task);
745           die_task =
746             GNUNET_SCHEDULER_add_now (&end_badly,
747                                       "from topology_callback (too many failed connections)");
748         }
749     }
750   else
751     {
752 #if VERBOSE > 1
753       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
754                   "Have %d total connections, %d failed connections, Want %d (at least %d)\n",
755                   total_connections, failed_connections, expected_connections,
756                   expected_connections -
757                   (unsigned int) (fail_percentage * expected_connections));
758 #endif
759     }
760 }
761
762 static void
763 topology_creation_finished (void *cls, const char *emsg)
764 {
765 #if VERBOSE
766   if (emsg == NULL)
767     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
768                 "All topology connections created successfully!\n");
769 #endif
770 }
771
772 static void
773 connect_topology ()
774 {
775   expected_connections = -1;
776   if ((pg != NULL) && (peers_left == 0))
777     {
778       expected_connections =
779         GNUNET_TESTING_connect_topology (pg, connection_topology,
780                                          connect_topology_option,
781                                          connect_topology_option_modifier,
782                                          connect_timeout,
783                                          connect_attempts,
784                                          &topology_creation_finished, NULL);
785 #if VERBOSE > 1
786       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
787                   "Have %d expected connections\n", expected_connections);
788 #endif
789     }
790
791   GNUNET_SCHEDULER_cancel (die_task);
792   if (expected_connections == GNUNET_SYSERR)
793     {
794       die_task =
795         GNUNET_SCHEDULER_add_now (&end_badly,
796                                   "from connect topology (bad return)");
797     }
798
799   die_task = GNUNET_SCHEDULER_add_delayed (test_timeout,
800                                            &end_badly,
801                                            "from connect topology (timeout)");
802   modnum = expected_connections / 4;
803   dotnum = (expected_connections / 50) + 1;
804 #if VERBOSE
805   fprintf (stdout, "Peer connection progress: [");
806 #endif
807 }
808
809 static void
810 create_topology ()
811 {
812   peers_left = num_peers;       /* Reset counter */
813   if (GNUNET_TESTING_create_topology
814       (pg, topology, blacklist_topology,
815        blacklist_transports) != GNUNET_SYSERR)
816     {
817 #if VERBOSE
818       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
819                   "Topology set up, now starting peers!\n");
820       fprintf (stdout, "Daemon start progress [");
821 #endif
822       GNUNET_TESTING_daemons_continue_startup (pg);
823     }
824   else
825     {
826       GNUNET_SCHEDULER_cancel (die_task);
827       die_task =
828         GNUNET_SCHEDULER_add_now (&end_badly,
829                                   "from create topology (bad return)");
830     }
831   GNUNET_SCHEDULER_cancel (die_task);
832   die_task = GNUNET_SCHEDULER_add_delayed (test_timeout,
833                                            &end_badly,
834                                            "from continue startup (timeout)");
835 }
836
837
838 static void
839 peers_started_callback (void *cls,
840                         const struct GNUNET_PeerIdentity *id,
841                         const struct GNUNET_CONFIGURATION_Handle *cfg,
842                         struct GNUNET_TESTING_Daemon *d, const char *emsg)
843 {
844   if (emsg != NULL)
845     {
846       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
847                   "Failed to start daemon with error: `%s'\n", emsg);
848       return;
849     }
850   GNUNET_assert (id != NULL);
851 #if VERBOSE > 1
852   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Started daemon %llu out of %llu\n",
853               (num_peers - peers_left) + 1, num_peers);
854 #endif
855 #if VERBOSE
856   if ((num_peers - peers_left) % modnum == 0)
857     {
858       if (num_peers - peers_left == 0)
859         fprintf (stdout, "0%%");
860       else
861         fprintf (stdout, "%d%%",
862                  (int) (((float) (num_peers - peers_left) /
863                          num_peers) * 100));
864
865     }
866   else if ((num_peers - peers_left) % dotnum == 0)
867     {
868       fprintf (stdout, ".");
869     }
870   fflush (stdout);
871 #endif
872   peers_left--;
873   if (peers_left == 0)
874     {
875 #if VERBOSE
876       fprintf (stdout, "100%%]\n");
877 #endif
878 #if VERBOSE
879       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
880                   "All %d daemons started, now connecting peers!\n",
881                   num_peers);
882 #endif
883       GNUNET_SCHEDULER_cancel (die_task);
884       /* Set up task in case topology creation doesn't finish
885        * within a reasonable amount of time */
886       die_task = GNUNET_SCHEDULER_add_delayed (timeout,
887                                                &end_badly,
888                                                "from peers_started_callback");
889 #if DELAY_FOR_LOGGING
890       fprintf (stdout, "Connecting topology in 10 seconds\n");
891       gather_log_data ();
892       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
893                                     (GNUNET_TIME_UNIT_SECONDS, 10),
894                                     &connect_topology, NULL);
895 #else
896       connect_topology ();
897 #endif
898       ok = 0;
899     }
900 }
901
902 /**
903  * Callback indicating that the hostkey was created for a peer.
904  *
905  * @param cls NULL
906  * @param id the peer identity
907  * @param d the daemon handle (pretty useless at this point, remove?)
908  * @param emsg non-null on failure
909  */
910 void
911 hostkey_callback (void *cls,
912                   const struct GNUNET_PeerIdentity *id,
913                   struct GNUNET_TESTING_Daemon *d, const char *emsg)
914 {
915   if (emsg != NULL)
916     {
917       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
918                   "Hostkey callback received error: %s\n", emsg);
919     }
920
921 #if VERBOSE > 1
922   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
923               "Hostkey (%d/%d) created for peer `%s'\n",
924               num_peers - peers_left, num_peers, GNUNET_i2s (id));
925 #endif
926
927 #if VERBOSE
928   if ((num_peers - peers_left) % modnum == 0)
929     {
930       if (num_peers - peers_left == 0)
931         fprintf (stdout, "0%%");
932       else
933         fprintf (stdout, "%d%%",
934                  (int) (((float) (num_peers - peers_left) /
935                          num_peers) * 100));
936
937     }
938   else if ((num_peers - peers_left) % dotnum == 0)
939     {
940       fprintf (stdout, ".");
941     }
942   fflush (stdout);
943 #endif
944   peers_left--;
945   if (peers_left == 0)
946     {
947 #if VERBOSE
948       fprintf (stdout, "100%%]\n");
949       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
950                   "All %d hostkeys created, now creating topology!\n",
951                   num_peers);
952 #endif
953       GNUNET_SCHEDULER_cancel (die_task);
954       /* Set up task in case topology creation doesn't finish
955        * within a reasonable amount of time */
956       die_task = GNUNET_SCHEDULER_add_delayed (test_timeout,
957                                                &end_badly,
958                                                "from create_topology");
959       GNUNET_SCHEDULER_add_now (&create_topology, NULL);
960       ok = 0;
961     }
962 }
963
964 static void
965 run (void *cls,
966      char *const *args,
967      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
968 {
969   char *topology_str;
970   char *connect_topology_str;
971   char *blacklist_topology_str;
972   char *connect_topology_option_str;
973   char *connect_topology_option_modifier_string;
974   unsigned long long temp_settle;
975   ok = 1;
976
977   dotOutFile = fopen (dotOutFileName, "w");
978   if (dotOutFile != NULL)
979     {
980       fprintf (dotOutFile, "strict graph G {\n");
981     }
982
983 #if VERBOSE
984   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
985               "Starting daemons based on config file %s\n", cfgfile);
986 #endif
987
988   if (GNUNET_YES !=
989       GNUNET_CONFIGURATION_get_value_string (cfg, "paths", "servicehome",
990                                              &test_directory))
991     {
992       ok = 404;
993       return;
994     }
995
996   if ((GNUNET_YES ==
997        GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "topology",
998                                               &topology_str))
999       && (GNUNET_NO == GNUNET_TESTING_topology_get (&topology, topology_str)))
1000     {
1001       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1002                   "Invalid topology `%s' given for section %s option %s\n",
1003                   topology_str, "TESTING", "TOPOLOGY");
1004       topology = GNUNET_TESTING_TOPOLOGY_CLIQUE;        /* Defaults to NONE, so set better default here */
1005     }
1006
1007   if ((GNUNET_YES ==
1008        GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
1009                                               "connect_topology",
1010                                               &connect_topology_str))
1011       && (GNUNET_NO ==
1012           GNUNET_TESTING_topology_get (&connection_topology,
1013                                        connect_topology_str)))
1014     {
1015       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1016                   "Invalid connect topology `%s' given for section %s option %s\n",
1017                   connect_topology_str, "TESTING", "CONNECT_TOPOLOGY");
1018     }
1019   GNUNET_free_non_null (connect_topology_str);
1020   if ((GNUNET_YES ==
1021        GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
1022                                               "connect_topology_option",
1023                                               &connect_topology_option_str))
1024       && (GNUNET_NO ==
1025           GNUNET_TESTING_topology_option_get (&connect_topology_option,
1026                                               connect_topology_option_str)))
1027     {
1028       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1029                   "Invalid connect topology option `%s' given for section %s option %s\n",
1030                   connect_topology_option_str, "TESTING",
1031                   "CONNECT_TOPOLOGY_OPTION");
1032       connect_topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_ALL;     /* Defaults to NONE, set to ALL */
1033     }
1034   GNUNET_free_non_null (connect_topology_option_str);
1035   if (GNUNET_YES ==
1036       GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
1037                                              "connect_topology_option_modifier",
1038                                              &connect_topology_option_modifier_string))
1039     {
1040       if (sscanf
1041           (connect_topology_option_modifier_string, "%lf",
1042            &connect_topology_option_modifier) != 1)
1043         {
1044           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1045                       _
1046                       ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1047                       connect_topology_option_modifier_string,
1048                       "connect_topology_option_modifier", "TESTING");
1049         }
1050       GNUNET_free (connect_topology_option_modifier_string);
1051     }
1052
1053   if (GNUNET_YES !=
1054       GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
1055                                              "blacklist_transports",
1056                                              &blacklist_transports))
1057     blacklist_transports = NULL;
1058
1059   if ((GNUNET_YES ==
1060        GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
1061                                               "blacklist_topology",
1062                                               &blacklist_topology_str))
1063       && (GNUNET_NO ==
1064           GNUNET_TESTING_topology_get (&blacklist_topology,
1065                                        blacklist_topology_str)))
1066     {
1067       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1068                   "Invalid topology `%s' given for section %s option %s\n",
1069                   topology_str, "TESTING", "BLACKLIST_TOPOLOGY");
1070     }
1071   GNUNET_free_non_null (topology_str);
1072   GNUNET_free_non_null (blacklist_topology_str);
1073
1074   if (GNUNET_OK ==
1075       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "settle_time",
1076                                              &temp_settle))
1077     settle_time =
1078       GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, temp_settle);
1079
1080   if (GNUNET_SYSERR ==
1081       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers",
1082                                              &num_peers))
1083     num_peers = DEFAULT_NUM_PEERS;
1084
1085   if (GNUNET_OK ==
1086       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "connect_timeout",
1087                                              &temp_settle))
1088     connect_timeout =
1089       GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, temp_settle);
1090   else
1091     {
1092       GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n", "testing", "connect_timeout");
1093       return;
1094     }
1095
1096
1097   if (GNUNET_OK !=
1098         GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "connect_attempts",
1099                                                &connect_attempts))
1100     {
1101       GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n", "testing", "connect_attempts");
1102       return;
1103     }
1104
1105   main_cfg = cfg;
1106
1107   peers_left = num_peers;
1108
1109   /**
1110    * How long until we fail the whole testcase?
1111    */
1112   test_timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, SECONDS_PER_PEER_START), num_peers * 2);
1113
1114   /**
1115    * How long until we give up on starting the peers?
1116    */
1117   timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, SECONDS_PER_PEER_START), num_peers);
1118
1119   modnum = num_peers / 4;
1120   dotnum = (num_peers / 50) + 1;
1121 #if VERBOSE
1122   fprintf (stdout, "Hostkey generation progress: [");
1123 #endif
1124   /* Set up a task to end testing if peer start fails */
1125   die_task =
1126     GNUNET_SCHEDULER_add_delayed (timeout,
1127                                   &end_badly,
1128                                   "didn't generate all hostkeys within a reasonable amount of time!!!");
1129
1130   GNUNET_assert (num_peers > 0 && num_peers < (unsigned int) -1);
1131   pg = GNUNET_TESTING_daemons_start (cfg,
1132                                      peers_left,
1133                                      peers_left / 2,
1134                                      peers_left,
1135                                      timeout,
1136                                      &hostkey_callback, NULL,
1137                                      &peers_started_callback, NULL,
1138                                      &topology_callback, NULL, NULL);
1139
1140 }
1141
1142 static int
1143 check ()
1144 {
1145   char *binary_name;
1146   char *config_file_name;
1147   GNUNET_asprintf (&binary_name, "test-testing-topology-%s", topology_string);
1148   GNUNET_asprintf (&config_file_name, "test_testing_data_topology_%s.conf",
1149                    topology_string);
1150   int ret;
1151   char *const argv[] = { binary_name,
1152     "-c",
1153     config_file_name,
1154 #if VERBOSE
1155     "-L", "DEBUG",
1156 #endif
1157     NULL
1158   };
1159   struct GNUNET_GETOPT_CommandLineOption options[] = {
1160     GNUNET_GETOPT_OPTION_END
1161   };
1162   ret = GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
1163                             argv, binary_name, "nohelp", options, &run, &ok);
1164   if (ret != GNUNET_OK)
1165     {
1166       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1167                   "`test-testing-topology-%s': Failed with error code %d\n",
1168                   topology_string, ret);
1169     }
1170   GNUNET_free (binary_name);
1171   GNUNET_free (config_file_name);
1172   return ok;
1173 }
1174
1175 int
1176 main (int argc, char *argv[])
1177 {
1178   int ret;
1179   char *binary_start_pos;
1180   char *our_binary_name;
1181
1182   binary_start_pos = rindex (argv[0], '/');
1183   GNUNET_assert (binary_start_pos != NULL);
1184   topology_string = strstr (binary_start_pos, "_topology");
1185   GNUNET_assert (topology_string != NULL);
1186   topology_string++;
1187   topology_string = strstr (topology_string, "_");
1188   GNUNET_assert (topology_string != NULL);
1189   topology_string++;
1190
1191   GNUNET_asprintf (&our_binary_name, "test-testing-large-topology_%s",
1192                    topology_string);
1193   GNUNET_asprintf (&dotOutFileName, "large_topology_%s.dot", topology_string);
1194
1195   GNUNET_log_setup (our_binary_name,
1196 #if VERBOSE
1197                     "DEBUG",
1198 #else
1199                     "WARNING",
1200 #endif
1201                     NULL);
1202   ret = check ();
1203
1204   /**
1205    * Need to remove base directory, subdirectories taken care
1206    * of by the testing framework.
1207    */
1208   if (GNUNET_DISK_directory_remove (test_directory) != GNUNET_OK)
1209     {
1210       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1211                   "Failed to remove testing directory %s\n", test_directory);
1212     }
1213   GNUNET_free (our_binary_name);
1214   return ret;
1215 }
1216
1217 /* end of test_testing_topology.c */