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