seaspidering
[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_NO
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 120
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   GNUNET_SCHEDULER_add_now (sched, &finish_testing, NULL);
274 }
275
276 /**
277  * Callback function to process statistic values.
278  *
279  * @param cls closure
280  * @param peer the peer the statistics belong to
281  * @param subsystem name of subsystem that created the statistic
282  * @param name the name of the datum
283  * @param value the current value
284  * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
285  * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
286  */
287 static int stats_print  (void *cls,
288                          const struct GNUNET_PeerIdentity *peer,
289                          const char *subsystem,
290                          const char *name,
291                          uint64_t value,
292                          int is_persistent)
293 {
294   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "%s:%s:%s -- %llu\n", GNUNET_i2s(peer), subsystem, name, value);
295   return GNUNET_OK;
296 }
297
298 static void topology_cb (void *cls,
299                   const struct GNUNET_PeerIdentity *first,
300                   const struct GNUNET_PeerIdentity *second,
301                   struct GNUNET_TIME_Relative latency,
302                   uint32_t distance,
303                   const char *emsg)
304 {
305   FILE *outfile;
306   outfile = cls;
307   if (first != NULL)
308   {
309     if (outfile != NULL)
310     {
311       fprintf(outfile, "\t\"%s\" -- ", GNUNET_i2s(first));
312       fprintf(outfile, "\"%s\";\n", GNUNET_i2s(second));
313     }
314     topology_connections++;
315   }
316   else
317     {
318       fprintf(stderr, "Finished iterating over topology, %d total connections!\n", topology_connections);
319       if (outfile != NULL)
320       {
321         fprintf(outfile, "}\n");
322         fclose(outfile);
323         GNUNET_TESTING_get_statistics(pg, &stats_finished, &stats_print, NULL);
324         //GNUNET_SCHEDULER_add_now (sched, &finish_testing, NULL);
325       }
326     }
327 }
328
329 static int
330 process_mtype (void *cls,
331                const struct GNUNET_PeerIdentity *peer,
332                const struct GNUNET_MessageHeader *message,
333                struct GNUNET_TIME_Relative latency,
334                uint32_t distance)
335 {
336   char *dotOutFileNameFinished;
337   FILE *dotOutFileFinished;
338   struct TestMessageContext *pos = cls;
339   struct GNUNET_TestMessage *msg = (struct GNUNET_TestMessage *)message;
340   if (pos->uid != ntohl(msg->uid))
341     return GNUNET_OK;
342
343 #if VERBOSE
344     if ((total_messages_received) % modnum == 0)
345       {
346         if (total_messages_received == 0)
347           fprintf (stdout, "0%%");
348         else
349           fprintf (stdout, "%d%%",
350                    (int) (((float) total_messages_received /
351                            expected_messages) * 100));
352
353       }
354     else if (total_messages_received % dotnum == 0)
355       {
356         fprintf (stdout, ".");
357       }
358     fflush (stdout);
359 #endif
360
361   total_messages_received++;
362 #if VERBOSE > 1
363   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
364               "Received message from `%4s', type %d.\n", GNUNET_i2s (peer), ntohs(message->type));
365   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
366               "Total messages received %d, expected %d.\n", total_messages_received, expected_messages);
367 #endif
368
369   if (total_messages_received == expected_messages)
370     {
371 #if VERBOSE
372       fprintf(stdout, "100%%]\n");
373 #endif
374       GNUNET_SCHEDULER_cancel (sched, die_task);
375       GNUNET_asprintf(&dotOutFileNameFinished, "%s.dot", "final_topology");
376       dotOutFileFinished = fopen (dotOutFileNameFinished, "w");
377       GNUNET_free(dotOutFileNameFinished);
378       if (dotOutFileFinished != NULL)
379       {
380         fprintf(dotOutFileFinished, "strict graph G {\n");
381       }
382       topology_connections = 0;
383       GNUNET_TESTING_get_topology (pg, &topology_cb, dotOutFileFinished);
384       //GNUNET_SCHEDULER_add_now (sched, &finish_testing, NULL);
385     }
386   else
387     {
388       pos->disconnect_task = GNUNET_SCHEDULER_add_now(sched, &disconnect_cores, pos);
389     }
390
391   return GNUNET_OK;
392 }
393
394 static void
395 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
396 {
397   char *msg = cls;
398   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
399               "End badly was called (%s)... stopping daemons.\n", msg);
400   struct TestMessageContext *pos;
401   struct TestMessageContext *free_pos;
402
403   pos = test_messages;
404   while (pos != NULL)
405     {
406       if (pos->peer1handle != NULL)
407         {
408           GNUNET_CORE_disconnect(pos->peer1handle);
409           pos->peer1handle = NULL;
410         }
411       if (pos->peer2handle != NULL)
412         {
413           GNUNET_CORE_disconnect(pos->peer2handle);
414           pos->peer2handle = NULL;
415         }
416       free_pos = pos;
417       pos = pos->next;
418       GNUNET_free(free_pos);
419     }
420
421 #if VERBOSE
422     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
423                 "Transmit_ready's scheduled %d, failed %d, transmit_ready's called %d\n", transmit_ready_scheduled, transmit_ready_failed, transmit_ready_called);
424     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
425                 "Total messages received %d, expected %d.\n", total_messages_received, expected_messages);
426 #endif
427
428   if (pg != NULL)
429     {
430       GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
431       ok = 7331;                /* Opposite of leet */
432     }
433   else
434     ok = 401;                   /* Never got peers started */
435
436   if (dotOutFile != NULL)
437     {
438       fprintf(dotOutFile, "}");
439       fclose(dotOutFile);
440     }
441 }
442
443 static size_t
444 transmit_ready (void *cls, size_t size, void *buf)
445 {
446   struct GNUNET_TestMessage *m;
447   struct TestMessageContext *pos = cls;
448
449   GNUNET_assert (buf != NULL);
450   m = (struct GNUNET_TestMessage *) buf;
451   m->header.type = htons (MTYPE);
452   m->header.size = htons (sizeof (struct GNUNET_TestMessage));
453   m->uid = htonl(pos->uid);
454   transmit_ready_called++;
455 #if VERBOSE > 1
456   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
457               "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);
458 #endif
459   return sizeof (struct GNUNET_TestMessage);
460 }
461
462
463 static struct GNUNET_CORE_MessageHandler no_handlers[] = {
464   {NULL, 0, 0}
465 };
466
467 static struct GNUNET_CORE_MessageHandler handlers[] = {
468   {&process_mtype, MTYPE, sizeof (struct GNUNET_TestMessage)},
469   {NULL, 0, 0}
470 };
471
472 static void
473 init_notify_peer2 (void *cls,
474              struct GNUNET_CORE_Handle *server,
475              const struct GNUNET_PeerIdentity *my_identity,
476              const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
477 {
478   struct TestMessageContext *pos = cls;
479
480 #if VERBOSE > 1
481   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
482               "Core connection to `%4s' established, scheduling message send\n",
483               GNUNET_i2s (my_identity));
484 #endif
485   total_server_connections++;
486
487   if (NULL == GNUNET_CORE_notify_transmit_ready (pos->peer1handle,
488                                                  0,
489                                                  TIMEOUT,
490                                                  &pos->peer2->id,
491                                                  sizeof (struct GNUNET_TestMessage),
492                                                  &transmit_ready, pos))
493     {
494       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
495                   "RECEIVED NULL when asking core (1) for transmission to peer `%4s'\n",
496                   GNUNET_i2s (&pos->peer2->id));
497       transmit_ready_failed++;
498     }
499   else
500     {
501       transmit_ready_scheduled++;
502     }
503 }
504
505
506 static void
507 init_notify_peer1 (void *cls,
508              struct GNUNET_CORE_Handle *server,
509              const struct GNUNET_PeerIdentity *my_identity,
510              const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
511 {
512   struct TestMessageContext *pos = cls;
513   total_server_connections++;
514
515 #if VERBOSE > 1
516   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
517               "Core connection to `%4s' established, setting up handles\n",
518               GNUNET_i2s (my_identity));
519 #endif
520
521   /*
522    * Connect to the receiving peer
523    */
524   pos->peer2handle = GNUNET_CORE_connect (sched,
525                                           pos->peer2->cfg,
526                                           TIMEOUT,
527                                           pos,
528                                           &init_notify_peer2,
529                                           NULL,
530                                           NULL, 
531                                           NULL, NULL,
532                                           GNUNET_YES, NULL, GNUNET_YES, handlers);
533
534 }
535
536
537 static void
538 send_test_messages (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
539 {
540   struct TestMessageContext *pos = cls;
541
542   if ((pos == test_messages) && (settle_time.rel_value > 0))
543     {
544       topology_connections = 0;
545       GNUNET_TESTING_get_topology (pg, &topology_cb, NULL);
546     }
547   if ((tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN) || (cls == NULL))
548     return;
549
550   if (die_task == GNUNET_SCHEDULER_NO_TASK)
551     {
552       die_task = GNUNET_SCHEDULER_add_delayed (sched,
553                                                TEST_TIMEOUT,
554                                                &end_badly, "from send test messages (timeout)");
555     }
556
557   if (total_server_connections >= MAX_OUTSTANDING_CONNECTIONS)
558     {
559       GNUNET_SCHEDULER_add_delayed (sched, GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1),
560                                     &send_test_messages, pos);
561       return; /* Otherwise we'll double schedule messages here! */
562     }
563
564   /*
565    * Connect to the sending peer
566    */
567   pos->peer1handle = GNUNET_CORE_connect (sched,
568                                           pos->peer1->cfg,
569                                           TIMEOUT,
570                                           pos,
571                                           &init_notify_peer1,
572                                           NULL, NULL,
573                                           NULL,
574                                           NULL,
575                                           GNUNET_NO, NULL, GNUNET_NO, no_handlers);
576
577   GNUNET_assert(pos->peer1handle != NULL);
578
579   if (total_server_connections < MAX_OUTSTANDING_CONNECTIONS)
580     {
581       GNUNET_SCHEDULER_add_now (sched,
582                                 &send_test_messages, pos->next);
583     }
584   else
585     {
586       GNUNET_SCHEDULER_add_delayed (sched, GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1),
587                                     &send_test_messages, pos->next);
588     }
589 }
590
591
592 void
593 topology_callback (void *cls,
594                    const struct GNUNET_PeerIdentity *first,
595                    const struct GNUNET_PeerIdentity *second,
596                    uint32_t distance,
597                    const struct GNUNET_CONFIGURATION_Handle *first_cfg,
598                    const struct GNUNET_CONFIGURATION_Handle *second_cfg,
599                    struct GNUNET_TESTING_Daemon *first_daemon,
600                    struct GNUNET_TESTING_Daemon *second_daemon,
601                    const char *emsg)
602 {
603   struct TestMessageContext *temp_context;
604   if (emsg == NULL)
605     {
606 #if VERBOSE
607     if ((total_connections) % modnum == 0)
608       {
609         if (total_connections == 0)
610           fprintf (stdout, "0%%");
611         else
612           fprintf (stdout, "%d%%",
613                    (int) (((float) total_connections /
614                            expected_connections) * 100));
615
616       }
617     else if (total_connections % dotnum == 0)
618       {
619         fprintf (stdout, ".");
620       }
621     fflush (stdout);
622 #endif
623       total_connections++;
624 #if VERBOSE > 1
625       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "connected peer %s to peer %s\n",
626                  first_daemon->shortname,
627                  second_daemon->shortname);
628 #endif
629       temp_context = GNUNET_malloc(sizeof(struct TestMessageContext));
630       temp_context->peer1 = first_daemon;
631       temp_context->peer2 = second_daemon;
632       temp_context->next = test_messages;
633       temp_context->uid = total_connections;
634       temp_context->disconnect_task = GNUNET_SCHEDULER_NO_TASK;
635       test_messages = temp_context;
636
637       expected_messages++;
638       if (dotOutFile != NULL)
639         fprintf(dotOutFile, "\tn%s -- n%s;\n", first_daemon->shortname, second_daemon->shortname);
640     }
641 #if VERBOSE
642   else
643     {
644       failed_connections++;
645       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to connect peer %s to peer %s with error :\n%s\n",
646                first_daemon->shortname,
647                second_daemon->shortname, emsg);
648     }
649 #endif
650
651   if (total_connections == expected_connections)
652     {
653 #if VERBOSE
654       fprintf(stdout, "100%%]\n");
655 #endif
656 #if VERBOSE
657       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
658                   "Created %d total connections, which is our target number!  Calling send messages.\n",
659                   total_connections);
660 #endif
661       modnum = expected_messages / 4;
662       dotnum = (expected_messages / 50) + 1;
663       GNUNET_SCHEDULER_cancel (sched, die_task);
664       die_task = GNUNET_SCHEDULER_NO_TASK;
665 #if DELAY_FOR_LOGGING
666       fprintf(stdout, "Sending test messages in 10 seconds.\n");
667       GNUNET_SCHEDULER_add_delayed (sched,
668                                     GNUNET_TIME_relative_multiply
669                                     (GNUNET_TIME_UNIT_SECONDS, 10),
670                                     &send_test_messages, test_messages);
671       gather_log_data();
672 #else
673       if (settle_time.rel_value > 0)
674         {
675           GNUNET_TESTING_get_topology (pg, &topology_cb, NULL);
676         }
677       GNUNET_SCHEDULER_add_delayed (sched, settle_time, &send_test_messages, test_messages);
678 #endif
679 #if VERBOSE
680       fprintf(stdout, "Test message progress: [");
681 #endif
682
683     }
684   else if (total_connections + failed_connections == expected_connections)
685     {
686       if (failed_connections < (unsigned int)(fail_percentage * total_connections))
687         {
688           GNUNET_SCHEDULER_cancel (sched, die_task);
689           die_task = GNUNET_SCHEDULER_NO_TASK;
690           GNUNET_SCHEDULER_add_now (sched, &send_test_messages, test_messages);
691         }
692       else
693         {
694           GNUNET_SCHEDULER_cancel (sched, die_task);
695           die_task = GNUNET_SCHEDULER_add_now (sched,
696                                                &end_badly, "from topology_callback (too many failed connections)");
697         }
698     }
699   else
700     {
701 #if VERBOSE > 1
702       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
703                   "Have %d total connections, %d failed connections, Want %d (at least %d)\n",
704                   total_connections, failed_connections, expected_connections, expected_connections - (unsigned int)(fail_percentage * expected_connections));
705 #endif
706     }
707 }
708
709 static void topology_creation_finished (void *cls,
710                                         const char *emsg)
711 {
712 #if VERBOSE
713   if (emsg == NULL)
714     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "All topology connections created successfully!\n");
715 #endif
716 }
717
718 static void
719 connect_topology ()
720 {
721   expected_connections = -1;
722   if ((pg != NULL) && (peers_left == 0))
723     {
724       expected_connections = GNUNET_TESTING_connect_topology (pg, connection_topology, connect_topology_option, connect_topology_option_modifier, &topology_creation_finished, NULL);
725 #if VERBOSE > 1
726       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
727                   "Have %d expected connections\n", expected_connections);
728 #endif
729     }
730
731   GNUNET_SCHEDULER_cancel (sched, die_task);
732   if (expected_connections == GNUNET_SYSERR)
733     {
734       die_task = GNUNET_SCHEDULER_add_now (sched,
735                                            &end_badly, "from connect topology (bad return)");
736     }
737
738   die_task = GNUNET_SCHEDULER_add_delayed (sched,
739                                            TEST_TIMEOUT,
740                                            &end_badly, "from connect topology (timeout)");
741   modnum = expected_connections / 4;
742   dotnum = (expected_connections / 50) + 1;
743 #if VERBOSE
744   fprintf(stdout, "Peer connection progress: [");
745 #endif
746 }
747
748 static void
749 create_topology ()
750 {
751   peers_left = num_peers; /* Reset counter */
752   if (GNUNET_TESTING_create_topology (pg, topology, blacklist_topology, blacklist_transports) != GNUNET_SYSERR)
753     {
754 #if VERBOSE
755       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
756                   "Topology set up, now starting peers!\n");
757       fprintf(stdout, "Daemon start progress [");
758 #endif
759       GNUNET_TESTING_daemons_continue_startup(pg);
760     }
761   else
762     {
763       GNUNET_SCHEDULER_cancel (sched, die_task);
764       die_task = GNUNET_SCHEDULER_add_now (sched,
765                                            &end_badly, "from create topology (bad return)");
766     }
767   GNUNET_SCHEDULER_cancel (sched, die_task);
768   die_task = GNUNET_SCHEDULER_add_delayed (sched,
769                                            TEST_TIMEOUT,
770                                            &end_badly, "from continue startup (timeout)");
771 }
772
773
774 static void
775 peers_started_callback (void *cls,
776        const struct GNUNET_PeerIdentity *id,
777        const struct GNUNET_CONFIGURATION_Handle *cfg,
778        struct GNUNET_TESTING_Daemon *d, const char *emsg)
779 {
780   if (emsg != NULL)
781     {
782       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to start daemon with error: `%s'\n",
783                   emsg);
784       return;
785     }
786   GNUNET_assert (id != NULL);
787 #if VERBOSE > 1
788   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Started daemon %llu out of %llu\n",
789               (num_peers - peers_left) + 1, num_peers);
790 #endif
791 #if VERBOSE
792     if ((num_peers - peers_left) % modnum == 0)
793       {
794         if (num_peers - peers_left == 0)
795           fprintf (stdout, "0%%");
796         else
797           fprintf (stdout, "%d%%",
798                    (int) (((float) (num_peers - peers_left) /
799                            num_peers) * 100));
800
801       }
802     else if ((num_peers - peers_left) % dotnum == 0)
803       {
804         fprintf (stdout, ".");
805       }
806     fflush (stdout);
807 #endif
808   peers_left--;
809   if (peers_left == 0)
810     {
811 #if VERBOSE
812       fprintf(stdout, "100%%]\n");
813 #endif
814 #if VERBOSE
815       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
816                   "All %d daemons started, now connecting peers!\n",
817                   num_peers);
818 #endif
819       GNUNET_SCHEDULER_cancel (sched, die_task);
820       /* Set up task in case topology creation doesn't finish
821        * within a reasonable amount of time */
822       die_task = GNUNET_SCHEDULER_add_delayed (sched,
823                                                GNUNET_TIME_relative_multiply
824                                                (GNUNET_TIME_UNIT_MINUTES, 8),
825                                                &end_badly, "from peers_started_callback");
826 #if DELAY_FOR_LOGGING
827       fprintf(stdout, "Connecting topology in 10 seconds\n");
828       gather_log_data();
829       GNUNET_SCHEDULER_add_delayed (sched,
830                                     GNUNET_TIME_relative_multiply
831                                     (GNUNET_TIME_UNIT_SECONDS, 10),
832                                     &connect_topology, NULL);
833 #else
834       connect_topology ();
835 #endif
836       ok = 0;
837     }
838 }
839
840 /**
841  * Callback indicating that the hostkey was created for a peer.
842  *
843  * @param cls NULL
844  * @param id the peer identity
845  * @param d the daemon handle (pretty useless at this point, remove?)
846  * @param emsg non-null on failure
847  */
848 void hostkey_callback (void *cls,
849                        const struct GNUNET_PeerIdentity *id,
850                        struct GNUNET_TESTING_Daemon *d,
851                        const char *emsg)
852 {
853   if (emsg != NULL)
854     {
855       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Hostkey callback received error: %s\n", emsg);
856     }
857
858 #if VERBOSE > 1
859     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
860                 "Hostkey (%d/%d) created for peer `%s'\n",
861                 num_peers - peers_left, num_peers, GNUNET_i2s(id));
862 #endif
863
864 #if VERBOSE
865     if ((num_peers - peers_left) % modnum == 0)
866       {
867         if (num_peers - peers_left == 0)
868           fprintf (stdout, "0%%");
869         else
870           fprintf (stdout, "%d%%",
871                    (int) (((float) (num_peers - peers_left) /
872                            num_peers) * 100));
873
874       }
875     else if ((num_peers - peers_left) % dotnum == 0)
876       {
877         fprintf (stdout, ".");
878       }
879     fflush (stdout);
880 #endif
881     peers_left--;
882     if (peers_left == 0)
883       {
884 #if VERBOSE
885         fprintf(stdout, "100%%]\n");
886         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
887                     "All %d hostkeys created, now creating topology!\n",
888                     num_peers);
889 #endif
890         GNUNET_SCHEDULER_cancel (sched, die_task);
891         /* Set up task in case topology creation doesn't finish
892          * within a reasonable amount of time */
893         die_task = GNUNET_SCHEDULER_add_delayed (sched,
894                                                  TIMEOUT,
895                                                  &end_badly, "from create_topology");
896         GNUNET_SCHEDULER_add_now(sched, &create_topology, NULL);
897         ok = 0;
898       }
899 }
900
901 static void
902 run (void *cls,
903      struct GNUNET_SCHEDULER_Handle *s,
904      char *const *args,
905      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
906 {
907   char * topology_str;
908   char * connect_topology_str;
909   char * blacklist_topology_str;
910   char * connect_topology_option_str;
911   char * connect_topology_option_modifier_string;
912   unsigned long long temp_settle;
913   sched = s;
914   ok = 1;
915
916   dotOutFile = fopen (dotOutFileName, "w");
917   if (dotOutFile != NULL)
918     {
919       fprintf (dotOutFile, "strict graph G {\n");
920     }
921
922 #if VERBOSE
923   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
924               "Starting daemons based on config file %s\n", cfgfile);
925 #endif
926
927   if (GNUNET_YES != GNUNET_CONFIGURATION_get_value_string(cfg, "paths", "servicehome", &test_directory))
928     {
929       ok = 404;
930       return;
931     }
932
933   if ((GNUNET_YES ==
934       GNUNET_CONFIGURATION_get_value_string(cfg, "testing", "topology",
935                                             &topology_str)) && (GNUNET_NO == GNUNET_TESTING_topology_get(&topology, topology_str)))
936     {
937       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
938                   "Invalid topology `%s' given for section %s option %s\n", topology_str, "TESTING", "TOPOLOGY");
939       topology = GNUNET_TESTING_TOPOLOGY_CLIQUE; /* Defaults to NONE, so set better default here */
940     }
941
942   if ((GNUNET_YES ==
943       GNUNET_CONFIGURATION_get_value_string(cfg, "testing", "connect_topology",
944                                             &connect_topology_str)) && (GNUNET_NO == GNUNET_TESTING_topology_get(&connection_topology, connect_topology_str)))
945     {
946       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
947                   "Invalid connect topology `%s' given for section %s option %s\n", connect_topology_str, "TESTING", "CONNECT_TOPOLOGY");
948     }
949   GNUNET_free_non_null(connect_topology_str);
950   if ((GNUNET_YES ==
951       GNUNET_CONFIGURATION_get_value_string(cfg, "testing", "connect_topology_option",
952                                             &connect_topology_option_str)) && (GNUNET_NO == GNUNET_TESTING_topology_option_get(&connect_topology_option, connect_topology_option_str)))
953     {
954       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
955                   "Invalid connect topology option `%s' given for section %s option %s\n", connect_topology_option_str, "TESTING", "CONNECT_TOPOLOGY_OPTION");
956       connect_topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_ALL; /* Defaults to NONE, set to ALL */
957     }
958   GNUNET_free_non_null(connect_topology_option_str);
959   if (GNUNET_YES ==
960         GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "connect_topology_option_modifier",
961                                                &connect_topology_option_modifier_string))
962     {
963       if (sscanf(connect_topology_option_modifier_string, "%lf", &connect_topology_option_modifier) != 1)
964       {
965         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
966         _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
967         connect_topology_option_modifier_string,
968         "connect_topology_option_modifier",
969         "TESTING");
970       }
971       GNUNET_free (connect_topology_option_modifier_string);
972     }
973
974   if (GNUNET_YES != GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "blacklist_transports",
975                                          &blacklist_transports))
976     blacklist_transports = NULL;
977
978   if ((GNUNET_YES ==
979       GNUNET_CONFIGURATION_get_value_string(cfg, "testing", "blacklist_topology",
980                                             &blacklist_topology_str)) && (GNUNET_NO == GNUNET_TESTING_topology_get(&blacklist_topology, blacklist_topology_str)))
981     {
982       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
983                   "Invalid topology `%s' given for section %s option %s\n", topology_str, "TESTING", "BLACKLIST_TOPOLOGY");
984     }
985   GNUNET_free_non_null(topology_str);
986   GNUNET_free_non_null(blacklist_topology_str);
987
988   if (GNUNET_OK ==
989       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "settle_time",
990                                              &temp_settle))
991     settle_time = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, temp_settle);
992
993   if (GNUNET_SYSERR ==
994       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers",
995                                              &num_peers))
996     num_peers = DEFAULT_NUM_PEERS;
997
998   main_cfg = cfg;
999
1000   peers_left = num_peers;
1001   modnum = num_peers / 4;
1002   dotnum = (num_peers / 50) + 1;
1003 #if VERBOSE
1004   fprintf (stdout, "Hostkey generation progress: [");
1005 #endif
1006   /* Set up a task to end testing if peer start fails */
1007   die_task = GNUNET_SCHEDULER_add_delayed (sched,
1008                                            GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, SECONDS_PER_PEER_START * num_peers),
1009                                            &end_badly, "didn't generate all hostkeys within a reasonable amount of time!!!");
1010
1011   GNUNET_assert(num_peers > 0 && num_peers < (unsigned int)-1);
1012   pg = GNUNET_TESTING_daemons_start (sched, cfg,
1013                                      peers_left, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, SECONDS_PER_PEER_START * num_peers), &hostkey_callback, NULL, &peers_started_callback, NULL,
1014                                      &topology_callback, NULL, NULL);
1015
1016 }
1017
1018 static int
1019 check ()
1020 {
1021   char *binary_name;
1022   char *config_file_name;
1023   GNUNET_asprintf(&binary_name, "test-testing-topology-%s", topology_string);
1024   GNUNET_asprintf(&config_file_name, "test_testing_data_topology_%s.conf", topology_string);
1025   int ret;
1026   char *const argv[] = {binary_name,
1027     "-c",
1028     config_file_name,
1029 #if VERBOSE
1030     "-L", "DEBUG",
1031 #endif
1032     NULL
1033   };
1034   struct GNUNET_GETOPT_CommandLineOption options[] = {
1035     GNUNET_GETOPT_OPTION_END
1036   };
1037   ret = GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
1038                       argv, binary_name, "nohelp",
1039                       options, &run, &ok);
1040   if (ret != GNUNET_OK)
1041     {
1042       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "`test-testing-topology-%s': Failed with error code %d\n", topology_string, ret);
1043     }
1044   GNUNET_free(binary_name);
1045   GNUNET_free(config_file_name);
1046   return ok;
1047 }
1048
1049 int
1050 main (int argc, char *argv[])
1051 {
1052   int ret;
1053   char *binary_start_pos;
1054   char *our_binary_name;
1055
1056   binary_start_pos = rindex(argv[0], '/');
1057   GNUNET_assert(binary_start_pos != NULL);
1058   topology_string = strstr (binary_start_pos,
1059                             "_topology");
1060   GNUNET_assert (topology_string != NULL);
1061   topology_string++;
1062   topology_string = strstr (topology_string, "_");
1063   GNUNET_assert (topology_string != NULL);
1064   topology_string++;
1065
1066   GNUNET_asprintf(&our_binary_name, "test-testing-topology_%s", topology_string);
1067   GNUNET_asprintf(&dotOutFileName, "topology_%s.dot", topology_string);
1068
1069   GNUNET_log_setup (our_binary_name,
1070 #if VERBOSE
1071                     "DEBUG",
1072 #else
1073                     "WARNING",
1074 #endif
1075                     NULL);
1076   ret = check ();
1077
1078   /**
1079    * Need to remove base directory, subdirectories taken care
1080    * of by the testing framework.
1081    */
1082   if (GNUNET_DISK_directory_remove (test_directory) != GNUNET_OK)
1083     {
1084       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to remove testing directory %s\n", test_directory);
1085     }
1086   GNUNET_free(our_binary_name);
1087   return ret;
1088 }
1089
1090 /* end of test_testing_topology.c */