2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
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.
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.
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.
21 * @file testing/test_testing_topology.c
22 * @brief base testcase for testing all the topologies provided
25 #include "gnunet_testing_lib.h"
26 #include "gnunet_core_service.h"
28 #define VERBOSE GNUNET_NO
31 * How long until we fail the whole testcase?
33 #define TEST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600)
36 * How long until we give up on starting the peers?
38 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 500)
40 #define DEFAULT_NUM_PEERS 4
42 #define MAX_OUTSTANDING_CONNECTIONS 300
44 static float fail_percentage = 0.05;
48 static unsigned long long num_peers;
50 static unsigned int total_connections;
52 static unsigned int failed_connections;
54 static unsigned int total_server_connections;
56 static unsigned int total_messages_received;
58 static unsigned int expected_messages;
60 static unsigned int expected_connections;
62 static unsigned long long peers_left;
64 static struct GNUNET_TESTING_PeerGroup *pg;
66 static struct GNUNET_SCHEDULER_Handle *sched;
68 const struct GNUNET_CONFIGURATION_Handle *main_cfg;
70 GNUNET_SCHEDULER_TaskIdentifier die_task;
72 static char *dotOutFileName;
74 static FILE *dotOutFile;
76 static char *topology_string;
78 static char *blacklist_transports;
80 static int transmit_ready_scheduled;
82 static int transmit_ready_failed;
84 static int transmit_ready_called;
86 static enum GNUNET_TESTING_Topology topology;
88 static enum GNUNET_TESTING_Topology blacklist_topology = GNUNET_TESTING_TOPOLOGY_NONE; /* Don't do any blacklisting */
90 static enum GNUNET_TESTING_Topology connection_topology = GNUNET_TESTING_TOPOLOGY_NONE; /* NONE actually means connect all allowed peers */
92 static enum GNUNET_TESTING_TopologyOption connect_topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_ALL;
94 static double connect_topology_option_modifier = 0.0;
96 static char *test_directory;
100 struct GNUNET_TestMessage
103 * Header of the message
105 struct GNUNET_MessageHeader header;
108 * Unique identifier for this message.
113 struct TestMessageContext
115 /* This is a linked list */
116 struct TestMessageContext *next;
118 /* Handle to the sending peer core */
119 struct GNUNET_CORE_Handle *peer1handle;
121 /* Handle to the receiving peer core */
122 struct GNUNET_CORE_Handle *peer2handle;
124 /* Handle to the sending peer daemon */
125 struct GNUNET_TESTING_Daemon *peer1;
127 /* Handle to the receiving peer daemon */
128 struct GNUNET_TESTING_Daemon *peer2;
130 /* Identifier for this message, so we don't disconnect other peers! */
133 /* Task for disconnecting cores, allow task to be cancelled on shutdown */
134 GNUNET_SCHEDULER_TaskIdentifier disconnect_task;
138 static struct TestMessageContext *test_messages;
143 GNUNET_assert (pg != NULL);
144 struct TestMessageContext *pos;
145 struct TestMessageContext *free_pos;
147 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
148 "Called finish testing, stopping daemons.\n");
155 if (pos->peer1handle != NULL)
157 GNUNET_CORE_disconnect(pos->peer1handle);
158 pos->peer1handle = NULL;
160 if (pos->peer2handle != NULL)
162 GNUNET_CORE_disconnect(pos->peer2handle);
163 pos->peer2handle = NULL;
167 if (free_pos->disconnect_task != GNUNET_SCHEDULER_NO_TASK)
169 GNUNET_SCHEDULER_cancel(sched, free_pos->disconnect_task);
171 GNUNET_free(free_pos);
174 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
175 "transmit_ready's scheduled %d, failed %d, transmit_ready's called %d\n", transmit_ready_scheduled, transmit_ready_failed, transmit_ready_called);
179 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
180 "Calling daemons_stop\n");
182 GNUNET_TESTING_daemons_stop (pg, TIMEOUT);
184 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
185 "daemons_stop finished\n");
187 if (dotOutFile != NULL)
189 fprintf(dotOutFile, "}");
198 disconnect_cores (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
200 struct TestMessageContext *pos = cls;
202 /* Disconnect from the respective cores */
204 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
205 "Disconnecting from peer 1 `%4s'\n", GNUNET_i2s (&pos->peer1->id));
207 if (pos->peer1handle != NULL)
208 GNUNET_CORE_disconnect(pos->peer1handle);
210 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
211 "Disconnecting from peer 2 `%4s'\n", GNUNET_i2s (&pos->peer2->id));
213 if (pos->peer2handle != NULL)
214 GNUNET_CORE_disconnect(pos->peer2handle);
215 /* Set handles to NULL so test case can be ended properly */
216 pos->peer1handle = NULL;
217 pos->peer2handle = NULL;
218 pos->disconnect_task = GNUNET_SCHEDULER_NO_TASK;
219 /* Decrement total connections so new can be established */
220 total_server_connections -= 2;
224 process_mtype (void *cls,
225 const struct GNUNET_PeerIdentity *peer,
226 const struct GNUNET_MessageHeader *message,
227 struct GNUNET_TIME_Relative latency,
230 struct TestMessageContext *pos = cls;
231 struct GNUNET_TestMessage *msg = (struct GNUNET_TestMessage *)message;
232 if (pos->uid != ntohl(msg->uid))
235 total_messages_received++;
237 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
238 "Received message from `%4s', type %d.\n", GNUNET_i2s (peer), ntohs(message->type));
239 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
240 "Total messages received %d, expected %d.\n", total_messages_received, expected_messages);
243 if (total_messages_received == expected_messages)
245 GNUNET_SCHEDULER_cancel (sched, die_task);
246 GNUNET_SCHEDULER_add_now (sched, &finish_testing, NULL);
250 pos->disconnect_task = GNUNET_SCHEDULER_add_now(sched, &disconnect_cores, pos);
257 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
260 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
261 "End badly was called (%s)... stopping daemons.\n", msg);
262 struct TestMessageContext *pos;
263 struct TestMessageContext *free_pos;
268 if (pos->peer1handle != NULL)
270 GNUNET_CORE_disconnect(pos->peer1handle);
271 pos->peer1handle = NULL;
273 if (pos->peer2handle != NULL)
275 GNUNET_CORE_disconnect(pos->peer2handle);
276 pos->peer2handle = NULL;
280 GNUNET_free(free_pos);
285 GNUNET_TESTING_daemons_stop (pg, TIMEOUT);
286 ok = 7331; /* Opposite of leet */
289 ok = 401; /* Never got peers started */
291 if (dotOutFile != NULL)
293 fprintf(dotOutFile, "}");
301 transmit_ready (void *cls, size_t size, void *buf)
303 struct GNUNET_TestMessage *m;
304 struct TestMessageContext *pos = cls;
306 GNUNET_assert (buf != NULL);
307 m = (struct GNUNET_TestMessage *) buf;
308 m->header.type = htons (MTYPE);
309 m->header.size = htons (sizeof (struct GNUNET_TestMessage));
310 m->uid = htonl(pos->uid);
311 transmit_ready_called++;
313 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
314 "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);
316 return sizeof (struct GNUNET_TestMessage);
320 static struct GNUNET_CORE_MessageHandler no_handlers[] = {
324 static struct GNUNET_CORE_MessageHandler handlers[] = {
325 {&process_mtype, MTYPE, sizeof (struct GNUNET_TestMessage)},
330 init_notify_peer2 (void *cls,
331 struct GNUNET_CORE_Handle *server,
332 const struct GNUNET_PeerIdentity *my_identity,
333 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
335 struct TestMessageContext *pos = cls;
338 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
339 "Core connection to `%4s' established, scheduling message send\n",
340 GNUNET_i2s (my_identity));
342 total_server_connections++;
344 if (NULL == GNUNET_CORE_notify_transmit_ready (pos->peer1handle,
348 sizeof (struct GNUNET_TestMessage),
349 &transmit_ready, pos))
351 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
352 "RECEIVED NULL when asking core (1) for transmission to peer `%4s'\n",
353 GNUNET_i2s (&pos->peer2->id));
354 transmit_ready_failed++;
358 transmit_ready_scheduled++;
364 init_notify_peer1 (void *cls,
365 struct GNUNET_CORE_Handle *server,
366 const struct GNUNET_PeerIdentity *my_identity,
367 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
369 struct TestMessageContext *pos = cls;
370 total_server_connections++;
373 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
374 "Core connection to `%4s' established, setting up handles\n",
375 GNUNET_i2s (my_identity));
379 * Connect to the receiving peer
381 pos->peer2handle = GNUNET_CORE_connect (sched,
389 GNUNET_YES, NULL, GNUNET_YES, handlers);
395 send_test_messages (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
397 struct TestMessageContext *pos = cls;
399 if ((tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN) || (cls == NULL))
402 if (die_task == GNUNET_SCHEDULER_NO_TASK)
404 die_task = GNUNET_SCHEDULER_add_delayed (sched,
406 &end_badly, "from create topology (timeout)");
409 if (total_server_connections >= MAX_OUTSTANDING_CONNECTIONS)
411 GNUNET_SCHEDULER_add_delayed (sched, GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1),
412 &send_test_messages, pos);
413 return; /* Otherwise we'll double schedule messages here! */
417 * Connect to the sending peer
419 pos->peer1handle = GNUNET_CORE_connect (sched,
427 GNUNET_NO, NULL, GNUNET_NO, no_handlers);
429 GNUNET_assert(pos->peer1handle != NULL);
431 if (total_server_connections < MAX_OUTSTANDING_CONNECTIONS)
433 GNUNET_SCHEDULER_add_now (sched,
434 &send_test_messages, pos->next);
438 GNUNET_SCHEDULER_add_delayed (sched, GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1),
439 &send_test_messages, pos->next);
445 topology_callback (void *cls,
446 const struct GNUNET_PeerIdentity *first,
447 const struct GNUNET_PeerIdentity *second,
448 const struct GNUNET_CONFIGURATION_Handle *first_cfg,
449 const struct GNUNET_CONFIGURATION_Handle *second_cfg,
450 struct GNUNET_TESTING_Daemon *first_daemon,
451 struct GNUNET_TESTING_Daemon *second_daemon,
454 struct TestMessageContext *temp_context;
459 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "connected peer %s to peer %s\n",
460 first_daemon->shortname,
461 second_daemon->shortname);
463 temp_context = GNUNET_malloc(sizeof(struct TestMessageContext));
464 temp_context->peer1 = first_daemon;
465 temp_context->peer2 = second_daemon;
466 temp_context->next = test_messages;
467 temp_context->uid = total_connections;
468 temp_context->disconnect_task = GNUNET_SCHEDULER_NO_TASK;
469 test_messages = temp_context;
472 if (dotOutFile != NULL)
473 fprintf(dotOutFile, "\tn%s -- n%s;\n", first_daemon->shortname, second_daemon->shortname);
478 failed_connections++;
479 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to connect peer %s to peer %s with error :\n%s\n",
480 first_daemon->shortname,
481 second_daemon->shortname, emsg);
485 if (total_connections == expected_connections)
488 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
489 "Created %d total connections, which is our target number! Calling send messages.\n",
493 GNUNET_SCHEDULER_cancel (sched, die_task);
494 die_task = GNUNET_SCHEDULER_NO_TASK;
495 GNUNET_SCHEDULER_add_now (sched, &send_test_messages, test_messages);
496 //GNUNET_SCHEDULER_add_delayed (sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 1), &send_test_messages, test_messages);
498 else if (total_connections + failed_connections == expected_connections)
500 if (failed_connections < (unsigned int)(fail_percentage * total_connections))
502 GNUNET_SCHEDULER_cancel (sched, die_task);
503 die_task = GNUNET_SCHEDULER_NO_TASK;
504 GNUNET_SCHEDULER_add_now (sched, &send_test_messages, test_messages);
505 //GNUNET_SCHEDULER_add_delayed (sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 1), &send_test_messages, test_messages);
509 GNUNET_SCHEDULER_cancel (sched, die_task);
510 die_task = GNUNET_SCHEDULER_add_now (sched,
511 &end_badly, "from topology_callback (too many failed connections)");
517 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
518 "Have %d total connections, %d failed connections, Want %d (at least %d)\n",
519 total_connections, failed_connections, expected_connections, expected_connections - (unsigned int)(fail_percentage * expected_connections));
527 expected_connections = -1;
528 if ((pg != NULL) && (peers_left == 0))
530 expected_connections = GNUNET_TESTING_connect_topology (pg, connection_topology, connect_topology_option, connect_topology_option_modifier);
532 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
533 "Have %d expected connections\n", expected_connections);
537 GNUNET_SCHEDULER_cancel (sched, die_task);
538 if (expected_connections == GNUNET_SYSERR)
540 die_task = GNUNET_SCHEDULER_add_now (sched,
541 &end_badly, "from connect topology (bad return)");
544 die_task = GNUNET_SCHEDULER_add_delayed (sched,
546 &end_badly, "from connect topology (timeout)");
552 peers_left = num_peers; /* Reset counter */
553 if (GNUNET_TESTING_create_topology (pg, topology, blacklist_topology, blacklist_transports) != GNUNET_SYSERR)
556 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
557 "Topology set up, now starting peers!\n");
559 GNUNET_TESTING_daemons_continue_startup(pg);
563 GNUNET_SCHEDULER_cancel (sched, die_task);
564 die_task = GNUNET_SCHEDULER_add_now (sched,
565 &end_badly, "from create topology (bad return)");
567 GNUNET_SCHEDULER_cancel (sched, die_task);
568 die_task = GNUNET_SCHEDULER_add_delayed (sched,
570 &end_badly, "from continue startup (timeout)");
575 peers_started_callback (void *cls,
576 const struct GNUNET_PeerIdentity *id,
577 const struct GNUNET_CONFIGURATION_Handle *cfg,
578 struct GNUNET_TESTING_Daemon *d, const char *emsg)
582 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to start daemon with error: `%s'\n",
586 GNUNET_assert (id != NULL);
588 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Started daemon %llu out of %llu\n",
589 (num_peers - peers_left) + 1, num_peers);
595 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
596 "All %d daemons started, now creating topology!\n",
599 GNUNET_SCHEDULER_cancel (sched, die_task);
600 /* Set up task in case topology creation doesn't finish
601 * within a reasonable amount of time */
602 die_task = GNUNET_SCHEDULER_add_delayed (sched,
603 GNUNET_TIME_relative_multiply
604 (GNUNET_TIME_UNIT_MINUTES, 5),
605 &end_badly, "from peers_started_callback");
613 * Callback indicating that the hostkey was created for a peer.
616 * @param id the peer identity
617 * @param d the daemon handle (pretty useless at this point, remove?)
618 * @param emsg non-null on failure
620 void hostkey_callback (void *cls,
621 const struct GNUNET_PeerIdentity *id,
622 struct GNUNET_TESTING_Daemon *d,
627 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Hostkey callback received error: %s\n", emsg);
631 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
632 "Hostkey created for peer `%s'\n",
639 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
640 "All %d hostkeys created, now creating topology!\n",
643 GNUNET_SCHEDULER_cancel (sched, die_task);
644 /* Set up task in case topology creation doesn't finish
645 * within a reasonable amount of time */
646 die_task = GNUNET_SCHEDULER_add_delayed (sched,
647 GNUNET_TIME_relative_multiply
648 (GNUNET_TIME_UNIT_MINUTES, 5),
649 &end_badly, "from hostkey_callback");
650 GNUNET_SCHEDULER_add_now(sched, &create_topology, NULL);
657 struct GNUNET_SCHEDULER_Handle *s,
659 const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
661 unsigned long long topology_num;
662 unsigned long long connect_topology_num;
663 unsigned long long blacklist_topology_num;
664 unsigned long long connect_topology_option_num;
665 char *connect_topology_option_modifier_string;
669 dotOutFile = fopen (dotOutFileName, "w");
670 if (dotOutFile != NULL)
672 fprintf (dotOutFile, "strict graph G {\n");
676 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
677 "Starting daemons based on config file %s\n", cfgfile);
680 if (GNUNET_YES != GNUNET_CONFIGURATION_get_value_string(cfg, "paths", "servicehome", &test_directory))
687 GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "topology",
689 topology = topology_num;
692 GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "connect_topology",
693 &connect_topology_num))
694 connection_topology = connect_topology_num;
697 GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "connect_topology_option",
698 &connect_topology_option_num))
699 connect_topology_option = connect_topology_option_num;
702 GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "connect_topology_option_modifier",
703 &connect_topology_option_modifier_string))
705 if (sscanf(connect_topology_option_modifier_string, "%lf", &connect_topology_option_modifier) != 1)
707 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
708 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
709 connect_topology_option_modifier_string,
710 "connect_topology_option_modifier",
713 GNUNET_free (connect_topology_option_modifier_string);
716 if (GNUNET_YES != GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "blacklist_transports",
717 &blacklist_transports))
718 blacklist_transports = NULL;
721 GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "blacklist_topology",
722 &blacklist_topology_num))
723 blacklist_topology = blacklist_topology_num;
726 GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers",
728 num_peers = DEFAULT_NUM_PEERS;
732 peers_left = num_peers;
734 /* Set up a task to end testing if peer start fails */
735 die_task = GNUNET_SCHEDULER_add_delayed (sched,
736 GNUNET_TIME_relative_multiply
737 (GNUNET_TIME_UNIT_MINUTES, 5),
738 &end_badly, "didn't start all daemons in reasonable amount of time!!!");
740 pg = GNUNET_TESTING_daemons_start (sched, cfg,
741 peers_left, TIMEOUT, &hostkey_callback, NULL, &peers_started_callback, NULL,
742 &topology_callback, NULL, NULL);
750 char *config_file_name;
751 GNUNET_asprintf(&binary_name, "test-testing-topology-%s", topology_string);
752 GNUNET_asprintf(&config_file_name, "test_testing_data_topology_%s.conf", topology_string);
754 char *const argv[] = {binary_name,
762 struct GNUNET_GETOPT_CommandLineOption options[] = {
763 GNUNET_GETOPT_OPTION_END
765 ret = GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
766 argv, binary_name, "nohelp",
768 if (ret != GNUNET_OK)
770 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "`test-testing-topology-%s': Failed with error code %d\n", topology_string, ret);
772 GNUNET_free(binary_name);
773 GNUNET_free(config_file_name);
778 main (int argc, char *argv[])
781 char *binary_start_pos;
782 char *our_binary_name;
784 binary_start_pos = rindex(argv[0], '/');
785 topology_string = strstr (binary_start_pos,
787 GNUNET_assert (topology_string != NULL);
789 topology_string = strstr (topology_string, "_");
790 GNUNET_assert (topology_string != NULL);
793 GNUNET_asprintf(&our_binary_name, "test-testing-topology_%s", topology_string);
794 GNUNET_asprintf(&dotOutFileName, "topology_%s.dot", topology_string);
796 GNUNET_log_setup (our_binary_name,
806 * Need to remove base directory, subdirectories taken care
807 * of by the testing framework.
809 if (GNUNET_DISK_directory_remove (test_directory) != GNUNET_OK)
811 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to remove testing directory %s\n", test_directory);
813 GNUNET_free(our_binary_name);
817 /* end of test_testing_topology.c */