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