some testing changes, including an api change that likely breaks things for others
[oweals/gnunet.git] / src / testing / test_testing_topology_blacklist.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 testing/test_testing_topology_blacklist.c
22  * @brief base testcase for testing transport level blacklisting
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 /**
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? (Must be longer than the connect timeout!)
37  */
38 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300)
39
40 #define DEFAULT_NUM_PEERS 4
41
42 #define MAX_OUTSTANDING_CONNECTIONS 300
43
44 static int ok;
45
46 static unsigned long long num_peers;
47
48 static unsigned int total_connections;
49
50 static unsigned int failed_connections;
51
52 static unsigned int expected_connections;
53
54 static unsigned int expected_failed_connections;
55
56 static unsigned long long peers_left;
57
58 static struct GNUNET_TESTING_PeerGroup *pg;
59
60 static struct GNUNET_SCHEDULER_Handle *sched;
61
62 const struct GNUNET_CONFIGURATION_Handle *main_cfg;
63
64 GNUNET_SCHEDULER_TaskIdentifier die_task;
65
66 static char *dotOutFileName;
67
68 static FILE *dotOutFile;
69
70 static char *blacklist_transports;
71
72 static enum GNUNET_TESTING_Topology topology = GNUNET_TESTING_TOPOLOGY_CLIQUE; /* Overlay should allow all connections */
73
74 static enum GNUNET_TESTING_Topology blacklist_topology = GNUNET_TESTING_TOPOLOGY_RING; /* Blacklist underlay into a ring */
75
76 static enum GNUNET_TESTING_Topology connection_topology = GNUNET_TESTING_TOPOLOGY_NONE; /* NONE actually means connect all allowed peers */
77
78 static enum GNUNET_TESTING_TopologyOption connect_topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_ALL; /* Try to connect all possible OVERLAY connections */
79
80 static double connect_topology_option_modifier = 0.0;
81
82 static char *test_directory;
83
84 #define MTYPE 12345
85
86 struct GNUNET_TestMessage
87 {
88   /**
89    * Header of the message
90    */
91   struct GNUNET_MessageHeader header;
92
93   /**
94    * Unique identifier for this message.
95    */
96   uint32_t uid;
97 };
98
99 static void
100 finish_testing ()
101 {
102   GNUNET_assert (pg != NULL);
103
104 #if VERBOSE
105   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
106               "Called finish testing, stopping daemons.\n");
107 #endif
108   sleep(1);
109 #if VERBOSE
110           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
111                       "Calling daemons_stop\n");
112 #endif
113   GNUNET_TESTING_daemons_stop (pg, TIMEOUT);
114 #if VERBOSE
115           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
116                       "daemons_stop finished\n");
117 #endif
118   if (dotOutFile != NULL)
119     {
120       fprintf(dotOutFile, "}");
121       fclose(dotOutFile);
122     }
123
124   ok = 0;
125 }
126
127 static void
128 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
129 {
130   char *msg = cls;
131   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
132               "End badly was called (%s)... stopping daemons.\n", msg);
133
134   if (pg != NULL)
135     {
136       GNUNET_TESTING_daemons_stop (pg, TIMEOUT);
137       ok = 7331;                /* Opposite of leet */
138     }
139   else
140     ok = 401;                   /* Never got peers started */
141
142   if (dotOutFile != NULL)
143     {
144       fprintf(dotOutFile, "}");
145       fclose(dotOutFile);
146     }
147 }
148
149
150
151 void
152 topology_callback (void *cls,
153                    const struct GNUNET_PeerIdentity *first,
154                    const struct GNUNET_PeerIdentity *second,
155                    const struct GNUNET_CONFIGURATION_Handle *first_cfg,
156                    const struct GNUNET_CONFIGURATION_Handle *second_cfg,
157                    struct GNUNET_TESTING_Daemon *first_daemon,
158                    struct GNUNET_TESTING_Daemon *second_daemon,
159                    const char *emsg)
160 {
161   if (emsg == NULL)
162     {
163       total_connections++;
164 #if VERBOSE
165       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "connected peer %s to peer %s\n",
166                first_daemon->shortname,
167                second_daemon->shortname);
168 #endif
169       if (dotOutFile != NULL)
170         fprintf(dotOutFile, "\tn%s -- n%s;\n", first_daemon->shortname, second_daemon->shortname);
171     }
172 #if VERBOSE
173   else
174     {
175       failed_connections++;
176       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to connect peer %s to peer %s with error :\n%s\n",
177                first_daemon->shortname,
178                second_daemon->shortname, emsg);
179     }
180 #endif
181
182   if (total_connections == expected_connections)
183     {
184 #if VERBOSE
185       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
186                   "Created %d total connections, which is our target number (that's bad)!\n",
187                   total_connections);
188 #endif
189
190       GNUNET_SCHEDULER_cancel (sched, die_task);
191       die_task = GNUNET_SCHEDULER_NO_TASK;
192       die_task = GNUNET_SCHEDULER_add_now (sched,
193                                            &end_badly, "from topology_callback (too many successful connections)");
194     }
195   else if (total_connections + failed_connections == expected_connections)
196     {
197       if ((failed_connections == expected_failed_connections) && (total_connections == expected_connections - expected_failed_connections))
198         {
199           GNUNET_SCHEDULER_cancel (sched, die_task);
200           die_task = GNUNET_SCHEDULER_NO_TASK;
201           die_task = GNUNET_SCHEDULER_add_now (sched,
202                                                &finish_testing, NULL);
203         }
204       else
205         {
206           GNUNET_SCHEDULER_cancel (sched, die_task);
207           die_task = GNUNET_SCHEDULER_add_now (sched,
208                                                &end_badly, "from topology_callback (wrong number of failed connections)");
209         }
210     }
211   else
212     {
213 #if VERBOSE
214       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
215                   "Have %d total connections, %d failed connections, Want %d (failed) and %d (successful)\n",
216                   total_connections, failed_connections, expected_failed_connections, expected_connections - expected_failed_connections);
217 #endif
218     }
219 }
220
221 static void
222 connect_topology ()
223 {
224   expected_connections = -1;
225   if ((pg != NULL) && (peers_left == 0))
226     {
227       expected_connections = GNUNET_TESTING_connect_topology (pg, connection_topology, connect_topology_option, connect_topology_option_modifier);
228 #if VERBOSE
229       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
230                   "Have %d expected connections\n", expected_connections);
231 #endif
232     }
233
234   GNUNET_SCHEDULER_cancel (sched, die_task);
235   if (expected_connections == GNUNET_SYSERR)
236     {
237       die_task = GNUNET_SCHEDULER_add_now (sched,
238                                            &end_badly, "from connect topology (bad return)");
239     }
240
241   die_task = GNUNET_SCHEDULER_add_delayed (sched,
242                                            TEST_TIMEOUT,
243                                            &end_badly, "from connect topology (timeout)");
244 }
245
246 static void
247 create_topology ()
248 {
249   peers_left = num_peers; /* Reset counter */
250   if (GNUNET_TESTING_create_topology (pg, topology, blacklist_topology, blacklist_transports) != GNUNET_SYSERR)
251     {
252 #if VERBOSE
253       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
254                   "Topology set up, now starting peers!\n");
255 #endif
256       GNUNET_TESTING_daemons_continue_startup(pg);
257     }
258   else
259     {
260       GNUNET_SCHEDULER_cancel (sched, die_task);
261       die_task = GNUNET_SCHEDULER_add_now (sched,
262                                            &end_badly, "from create topology (bad return)");
263     }
264   GNUNET_SCHEDULER_cancel (sched, die_task);
265   die_task = GNUNET_SCHEDULER_add_delayed (sched,
266                                            TEST_TIMEOUT,
267                                            &end_badly, "from continue startup (timeout)");
268 }
269
270
271 static void
272 peers_started_callback (void *cls,
273        const struct GNUNET_PeerIdentity *id,
274        const struct GNUNET_CONFIGURATION_Handle *cfg,
275        struct GNUNET_TESTING_Daemon *d, const char *emsg)
276 {
277   if (emsg != NULL)
278     {
279       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to start daemon with error: `%s'\n",
280                   emsg);
281       return;
282     }
283   GNUNET_assert (id != NULL);
284 #if VERBOSE
285   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Started daemon %llu out of %llu\n",
286               (num_peers - peers_left) + 1, num_peers);
287 #endif
288   peers_left--;
289   if (peers_left == 0)
290     {
291 #if VERBOSE
292       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
293                   "All %d daemons started, now creating topology!\n",
294                   num_peers);
295 #endif
296       GNUNET_SCHEDULER_cancel (sched, die_task);
297       /* Set up task in case topology creation doesn't finish
298        * within a reasonable amount of time */
299       die_task = GNUNET_SCHEDULER_add_delayed (sched,
300                                                GNUNET_TIME_relative_multiply
301                                                (GNUNET_TIME_UNIT_MINUTES, 5),
302                                                &end_badly, "from peers_started_callback");
303       connect_topology ();
304       ok = 0;
305     }
306 }
307
308 /**
309  * Callback indicating that the hostkey was created for a peer.
310  *
311  * @param cls NULL
312  * @param id the peer identity
313  * @param d the daemon handle (pretty useless at this point, remove?)
314  * @param emsg non-null on failure
315  */
316 void hostkey_callback (void *cls,
317                        const struct GNUNET_PeerIdentity *id,
318                        struct GNUNET_TESTING_Daemon *d,
319                        const char *emsg)
320 {
321   if (emsg != NULL)
322     {
323       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Hostkey callback received error: %s\n", emsg);
324     }
325
326 #if VERBOSE
327     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
328                 "Hostkey created for peer `%s'\n",
329                 GNUNET_i2s(id));
330 #endif
331     peers_left--;
332     if (peers_left == 0)
333       {
334 #if VERBOSE
335         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
336                     "All %d hostkeys created, now creating topology!\n",
337                     num_peers);
338 #endif
339         GNUNET_SCHEDULER_cancel (sched, die_task);
340         /* Set up task in case topology creation doesn't finish
341          * within a reasonable amount of time */
342         die_task = GNUNET_SCHEDULER_add_delayed (sched,
343                                                  GNUNET_TIME_relative_multiply
344                                                  (GNUNET_TIME_UNIT_MINUTES, 5),
345                                                  &end_badly, "from hostkey_callback");
346         GNUNET_SCHEDULER_add_now(sched, &create_topology, NULL);
347         ok = 0;
348       }
349 }
350
351 static void
352 run (void *cls,
353      struct GNUNET_SCHEDULER_Handle *s,
354      char *const *args,
355      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
356 {
357   unsigned long long topology_num;
358   unsigned long long connect_topology_num;
359   unsigned long long blacklist_topology_num;
360   unsigned long long connect_topology_option_num;
361   char *connect_topology_option_modifier_string;
362   sched = s;
363   ok = 1;
364
365   dotOutFile = fopen (dotOutFileName, "w");
366   if (dotOutFile != NULL)
367     {
368       fprintf (dotOutFile, "strict graph G {\n");
369     }
370
371 #if VERBOSE
372   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
373               "Starting daemons based on config file %s\n", cfgfile);
374 #endif
375
376   if (GNUNET_YES != GNUNET_CONFIGURATION_get_value_string(cfg, "paths", "servicehome", &test_directory))
377     {
378       ok = 404;
379       return;
380     }
381
382   if (GNUNET_YES ==
383       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "topology",
384                                              &topology_num))
385     topology = topology_num;
386
387   if (GNUNET_YES ==
388       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "connect_topology",
389                                              &connect_topology_num))
390     connection_topology = connect_topology_num;
391
392   if (GNUNET_YES ==
393         GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "connect_topology_option",
394                                                &connect_topology_option_num))
395     connect_topology_option = connect_topology_option_num;
396
397   if (GNUNET_YES ==
398         GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "connect_topology_option_modifier",
399                                                &connect_topology_option_modifier_string))
400     {
401       if (sscanf(connect_topology_option_modifier_string, "%lf", &connect_topology_option_modifier) != 1)
402       {
403         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
404         _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
405         connect_topology_option_modifier_string,
406         "connect_topology_option_modifier",
407         "TESTING");
408         GNUNET_free (connect_topology_option_modifier_string);
409       }
410     }
411
412   GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "blacklist_transports",
413                                          &blacklist_transports);
414
415   if (GNUNET_YES ==
416       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "blacklist_topology",
417                                              &blacklist_topology_num))
418     blacklist_topology = blacklist_topology_num;
419
420   if (GNUNET_SYSERR ==
421       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers",
422                                              &num_peers))
423     num_peers = DEFAULT_NUM_PEERS;
424
425   main_cfg = cfg;
426
427   peers_left = num_peers;
428
429   /* For this specific test we only really want a CLIQUE topology as the
430    * overlay allowed topology, and a RING topology as the underlying connection
431    * allowed topology.  So we will expect only num_peers * 2 connections to
432    * work, and (num_peers * (num_peers - 1)) - (num_peers * 2) to fail.
433    */
434   expected_connections = num_peers * (num_peers - 1);
435   expected_failed_connections = expected_connections - (num_peers * 2);
436
437
438   /* Set up a task to end testing if peer start fails */
439   die_task = GNUNET_SCHEDULER_add_delayed (sched,
440                                            GNUNET_TIME_relative_multiply
441                                            (GNUNET_TIME_UNIT_MINUTES, 5),
442                                            &end_badly, "didn't start all daemons in reasonable amount of time!!!");
443
444   pg = GNUNET_TESTING_daemons_start (sched, cfg,
445                                      peers_left, TIMEOUT, &hostkey_callback, NULL, &peers_started_callback, NULL,
446                                      &topology_callback, NULL, NULL);
447
448 }
449
450 static int
451 check ()
452 {
453   int ret;
454   char *const argv[] = {"test-testing-topology-blacklist",
455     "-c",
456     "test_testing_data_topology_blacklist.conf",
457 #if VERBOSE
458     "-L", "DEBUG",
459 #endif
460     NULL
461   };
462   struct GNUNET_GETOPT_CommandLineOption options[] = {
463     GNUNET_GETOPT_OPTION_END
464   };
465   ret = GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
466                       argv, "test-testing-topology-blacklist", "nohelp",
467                       options, &run, &ok);
468   if (ret != GNUNET_OK)
469     {
470       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "`test-testing-topology-blacklist': Failed with error code %d\n", ret);
471     }
472
473   return ok;
474 }
475
476 int
477 main (int argc, char *argv[])
478 {
479   int ret;
480
481   GNUNET_log_setup ("test_testing_topology_blacklist",
482 #if VERBOSE
483                     "DEBUG",
484 #else
485                     "WARNING",
486 #endif
487                     NULL);
488   ret = check ();
489
490   /**
491    * Need to remove base directory, subdirectories taken care
492    * of by the testing framework.
493    */
494   if (test_directory != NULL)
495     {
496       if (GNUNET_DISK_directory_remove (test_directory) != GNUNET_OK)
497         {
498           GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to remove testing directory %s\n", test_directory);
499         }
500     }
501
502   return ret;
503 }
504
505 /* end of test_testing_topology_blacklist.c */