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