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