eae59913c4c53494a60e0fc1abf5be4c05271c64
[oweals/gnunet.git] / src / testing / test_testing_topology.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 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.c
22  * @brief base testcase for testing all the topologies provided
23  */
24 #include "platform.h"
25 #include "gnunet_testing_lib.h"
26 #include "gnunet_core_service.h"
27
28 #define VERBOSE 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 connecting the peers?
37  */
38 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
39
40 #define DEFAULT_NUM_PEERS 4;
41
42 static float fail_percentage = 0.05;
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 total_server_connections;
53
54 static unsigned int total_messages_received;
55
56 static unsigned int expected_messages;
57
58 static unsigned int expected_connections;
59
60 static unsigned long long peers_left;
61
62 static struct GNUNET_TESTING_PeerGroup *pg;
63
64 static struct GNUNET_SCHEDULER_Handle *sched;
65
66 const struct GNUNET_CONFIGURATION_Handle *main_cfg;
67
68 GNUNET_SCHEDULER_TaskIdentifier die_task;
69
70 static char *dotOutFileName = "topology.dot";
71
72 static FILE *dotOutFile;
73
74 static char *topology_string;
75
76 static int transmit_ready_scheduled;
77
78 static int transmit_ready_called;
79
80 struct TestMessageContext *global_pos;
81
82 #define MTYPE 12345
83
84 struct TestMessageContext
85 {
86   /* This is a linked list */
87   struct TestMessageContext *next;
88
89   /* Handle to the sending peer core */
90   struct GNUNET_CORE_Handle *peer1handle;
91
92   /* Handle to the receiving peer core */
93   struct GNUNET_CORE_Handle *peer2handle;
94
95   /* Handle to the sending peer daemon */
96   struct GNUNET_TESTING_Daemon *peer1;
97
98   /* Handle to the receiving peer daemon */
99   struct GNUNET_TESTING_Daemon *peer2;
100
101   /* Maintain some state */
102   int first_step_done;
103
104 };
105
106 struct Connection
107 {
108   struct Connection *next;
109   struct GNUNET_TESTING_Daemon *peer;
110   struct GNUNET_CORE_Handle *server;
111 };
112
113 static struct Connection *global_connections;
114
115 static struct TestMessageContext *test_messages;
116
117 static void
118 finish_testing ()
119 {
120   GNUNET_assert (pg != NULL);
121   struct Connection *pos;
122 #if VERBOSE
123   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
124               "Called finish testing, stopping daemons.\n");
125 #endif
126   int count;
127   count = 0;
128   pos = global_connections;
129   while (pos != NULL)
130     {
131       if (pos->server != NULL)
132         {
133           GNUNET_CORE_disconnect(pos->server);
134           pos->server = NULL;
135         }
136       pos = pos->next;
137     }
138 #if VERBOSE
139           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
140                       "transmit_ready's scheduled %d, transmit_ready's called %d\n", transmit_ready_scheduled, transmit_ready_called);
141 #endif
142   sleep(1);
143 #if VERBOSE
144           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
145                       "Calling daemons_stop\n");
146 #endif
147   GNUNET_TESTING_daemons_stop (pg);
148 #if VERBOSE
149           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
150                       "daemons_stop finished\n");
151 #endif
152   if (dotOutFile != NULL)
153     {
154       fprintf(dotOutFile, "}");
155       fclose(dotOutFile);
156     }
157
158   ok = 0;
159 }
160
161 static int
162 process_mtype (void *cls,
163                const struct GNUNET_PeerIdentity *peer,
164                const struct GNUNET_MessageHeader *message,
165                struct GNUNET_TIME_Relative latency,
166                uint32_t distance)
167 {
168   total_messages_received++;
169 #if VERBOSE
170   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
171               "Received message from `%4s', type %d.\n", GNUNET_i2s (peer), ntohs(message->type));
172   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
173               "Total messages received %d, expected %d.\n", total_messages_received, expected_messages);
174 #endif
175
176   if (total_messages_received == expected_messages)
177     {
178       GNUNET_SCHEDULER_cancel (sched, die_task);
179       GNUNET_SCHEDULER_add_now (sched, &finish_testing, NULL);
180     }
181   return GNUNET_OK;
182 }
183
184 static void
185 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
186 {
187   char *msg = cls;
188   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
189               "End badly was called (%s)... stopping daemons.\n", msg);
190   struct Connection *pos;
191
192   pos = global_connections;
193   while (pos != NULL)
194     {
195       if (pos->server != NULL)
196         {
197           GNUNET_CORE_disconnect(pos->server);
198           pos->server = NULL;
199         }
200       pos = pos->next;
201     }
202
203   if (pg != NULL)
204     {
205       GNUNET_TESTING_daemons_stop (pg);
206       ok = 7331;                /* Opposite of leet */
207     }
208   else
209     ok = 401;                   /* Never got peers started */
210
211   if (dotOutFile != NULL)
212     {
213       fprintf(dotOutFile, "}");
214       fclose(dotOutFile);
215     }
216 }
217
218
219 /**
220  * Forward declaration.
221  */
222 static size_t
223 transmit_ready (void *cls, size_t size, void *buf);
224
225 static void
226 schedule_transmission (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
227 {
228
229   if (global_pos != NULL)
230   {
231     if (NULL == GNUNET_CORE_notify_transmit_ready (global_pos->peer1handle,
232                                                  0,
233                                                  TIMEOUT,
234                                                  &global_pos->peer2->id,
235                                                  sizeof (struct GNUNET_MessageHeader),
236                                                  &transmit_ready, &global_pos->peer1->id))
237       {
238         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
239                     "RECEIVED NULL when asking core (1) for transmission to peer `%4s'\n",
240                     GNUNET_i2s (&global_pos->peer2->id));
241       }
242     else
243       {
244         transmit_ready_scheduled++;
245       }
246     global_pos = global_pos->next;
247   }
248   else
249   {
250     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
251                 "Transmit ready scheduled on %d messages\n",
252                 transmit_ready_scheduled);
253   }
254
255 }
256
257 static size_t
258 transmit_ready (void *cls, size_t size, void *buf)
259 {
260   struct GNUNET_MessageHeader *m;
261 #if VERBOSE
262   struct GNUNET_PeerIdentity *peer = cls;
263 #endif
264   GNUNET_assert (buf != NULL);
265   m = (struct GNUNET_MessageHeader *) buf;
266   m->type = htons (MTYPE);
267   m->size = htons (sizeof (struct GNUNET_MessageHeader));
268
269   transmit_ready_called++;
270 #if VERBOSE
271   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
272               "transmit ready for peer %s\ntransmit_ready's scheduled %d, transmit_ready's called %d\n", GNUNET_i2s(peer), transmit_ready_scheduled, transmit_ready_called);
273 #endif
274   GNUNET_SCHEDULER_add_delayed(sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 50), &schedule_transmission, NULL);
275   return sizeof (struct GNUNET_MessageHeader);
276 }
277
278
279 static struct GNUNET_CORE_MessageHandler handlers[] = {
280   {&process_mtype, MTYPE, sizeof (struct GNUNET_MessageHeader)},
281   {NULL, 0, 0}
282 };
283
284
285
286 static void
287 send_test_messages ()
288 {
289   struct TestMessageContext *pos;
290   struct Connection *conn_pos;
291   die_task = GNUNET_SCHEDULER_add_delayed (sched,
292                                            TEST_TIMEOUT,
293                                            &end_badly, "from send test messages");
294
295   int count = 0;
296   int conn_count = 0;
297   pos = test_messages;
298   while (pos != NULL)
299     {
300       conn_pos = global_connections;
301       conn_count = 0;
302       while (conn_pos != NULL)
303         {
304           if (conn_pos->peer == pos->peer1)
305             {
306               pos->peer1handle = conn_pos->server;
307               break;
308             }
309           conn_count++;
310           conn_pos = conn_pos->next;
311         }
312       GNUNET_assert(pos->peer1handle != NULL);
313
314       /*
315       if (NULL == GNUNET_CORE_notify_transmit_ready (pos->peer1handle,
316                                                    0,
317                                                    TIMEOUT,
318                                                    &pos->peer2->id,
319                                                    sizeof (struct GNUNET_MessageHeader),
320                                                    &transmit_ready, &pos->peer1->id))
321         {
322           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
323                       "RECEIVED NULL when asking core (1) for transmission to peer `%4s'\n",
324                       GNUNET_i2s (&pos->peer2->id));
325         }
326       else
327         {
328           transmit_ready_scheduled++;
329         }
330       */
331       pos = pos->next;
332       count++;
333
334     }
335
336   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
337               "Prepared %d messages\n",
338               count);
339   global_pos = test_messages;
340
341   GNUNET_SCHEDULER_add_now(sched, &schedule_transmission, NULL);
342 }
343
344
345
346 static void
347 init_notify (void *cls,
348              struct GNUNET_CORE_Handle *server,
349              const struct GNUNET_PeerIdentity *my_identity,
350              const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
351 {
352   struct Connection *connection = cls;
353
354 #if VERBOSE
355   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
356               "Core connection to `%4s' established, setting up handles\n",
357               GNUNET_i2s (my_identity));
358 #endif
359   connection->server = server;
360   total_server_connections++;
361
362   if (total_server_connections == num_peers)
363     {
364       GNUNET_SCHEDULER_cancel(sched, die_task);
365       GNUNET_SCHEDULER_add_now(sched, &send_test_messages, NULL);
366     }
367 }
368
369
370 static void
371 setup_handlers ()
372 {
373   int i;
374   struct Connection *new_connection;
375
376   struct GNUNET_TESTING_Daemon *temp_daemon;
377   die_task = GNUNET_SCHEDULER_add_delayed (sched,
378                                            TEST_TIMEOUT,
379                                            &end_badly, "from setup_handlers");
380
381
382   /**
383    * Set up a single handler for each peer
384    */
385   for (i = 0; i < num_peers; i++)
386     {
387       new_connection = GNUNET_malloc(sizeof(struct Connection));
388       temp_daemon = GNUNET_TESTING_daemon_get(pg, i);
389       new_connection->peer = temp_daemon;
390       new_connection->server = NULL;
391       new_connection->next = global_connections;
392       global_connections = new_connection;
393
394       GNUNET_CORE_connect (sched,
395                            temp_daemon->cfg,
396                            TIMEOUT,
397                            new_connection,
398                            &init_notify,                         
399                            NULL,
400                            NULL,
401                            NULL,
402                            GNUNET_YES, NULL, GNUNET_YES, handlers);
403     }
404 }
405
406
407
408 void
409 topology_callback (void *cls,
410                    const struct GNUNET_PeerIdentity *first,
411                    const struct GNUNET_PeerIdentity *second,
412                    const struct GNUNET_CONFIGURATION_Handle *first_cfg,
413                    const struct GNUNET_CONFIGURATION_Handle *second_cfg,
414                    struct GNUNET_TESTING_Daemon *first_daemon,
415                    struct GNUNET_TESTING_Daemon *second_daemon,
416                    const char *emsg)
417 {
418   struct TestMessageContext *temp_context;
419   if (emsg == NULL)
420     {
421       total_connections++;
422 #if VERBOSE
423       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "connected peer %s to peer %s\n",
424                first_daemon->shortname,
425                second_daemon->shortname);
426 #endif
427       temp_context = GNUNET_malloc(sizeof(struct TestMessageContext));
428       temp_context->first_step_done = 0;
429       temp_context->peer1handle = NULL;
430       temp_context->peer1 = first_daemon;
431       temp_context->peer2 = second_daemon;
432       temp_context->peer2handle = NULL;
433       temp_context->next = test_messages;
434       test_messages = temp_context;
435
436       expected_messages++;
437       if (dotOutFile != NULL)
438         fprintf(dotOutFile, "\tn%s -- n%s;\n", first_daemon->shortname, second_daemon->shortname);
439     }
440 #if VERBOSE
441   else
442     {
443       failed_connections++;
444       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to connect peer %s to peer %s with error :\n%s\n",
445                first_daemon->shortname,
446                second_daemon->shortname, emsg);
447     }
448 #endif
449
450   if (total_connections == expected_connections)
451     {
452 #if VERBOSE
453       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
454                   "Created %d total connections, which is our target number!  Calling send messages.\n",
455                   total_connections);
456 #endif
457
458       GNUNET_SCHEDULER_cancel (sched, die_task);
459       /* die_task = GNUNET_SCHEDULER_add_now (sched, &setup_handlers, NULL); */
460       die_task = GNUNET_SCHEDULER_add_delayed (sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 1), &setup_handlers, NULL);
461     }
462   else if (total_connections + failed_connections == expected_connections)
463     {
464       if (failed_connections < (unsigned int)(fail_percentage * total_connections))
465         {
466           GNUNET_SCHEDULER_cancel (sched, die_task);
467           /* die_task = GNUNET_SCHEDULER_add_now (sched, &setup_handlers, NULL); */
468           die_task = GNUNET_SCHEDULER_add_delayed (sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 1), &setup_handlers, NULL);
469         }
470       else
471         {
472           GNUNET_SCHEDULER_cancel (sched, die_task);
473           die_task = GNUNET_SCHEDULER_add_now (sched,
474                                                &end_badly, "from topology_callback (too many failed connections)");
475         }
476     }
477   else
478     {
479 #if VERBOSE
480       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
481                   "Have %d total connections, %d failed connections, Want %d (at least %d)\n",
482                   total_connections, failed_connections, expected_connections, expected_connections - (unsigned int)(fail_percentage * expected_connections));
483 #endif
484     }
485 }
486
487
488 static void
489 create_topology ()
490 {
491   expected_connections = -1;
492   if ((pg != NULL) && (peers_left == 0))
493     {
494       /* create_topology will read the topology information from
495          the config already contained in the peer group, so should
496          we have create_topology called from start peers?  I think
497          maybe this way is best so that the client can know both
498          when peers are started, and when they are connected.
499        */
500       expected_connections = GNUNET_TESTING_create_topology (pg);
501 #if VERBOSE
502       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
503                   "Have %d expected connections\n", expected_connections);
504 #endif
505     }
506
507   GNUNET_SCHEDULER_cancel (sched, die_task);
508   if (expected_connections == GNUNET_SYSERR)
509     {
510       die_task = GNUNET_SCHEDULER_add_now (sched,
511                                            &end_badly, "from create topology (bad return)");
512     }
513   die_task = GNUNET_SCHEDULER_add_delayed (sched,
514                                            TEST_TIMEOUT,
515                                            &end_badly, "from create topology (timeout)");
516 }
517
518
519 static void
520 my_cb (void *cls,
521        const struct GNUNET_PeerIdentity *id,
522        const struct GNUNET_CONFIGURATION_Handle *cfg,
523        struct GNUNET_TESTING_Daemon *d, const char *emsg)
524 {
525   GNUNET_assert (id != NULL);
526 #if VERBOSE
527   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Started daemon %llu out of %llu\n",
528               (num_peers - peers_left) + 1, num_peers);
529 #endif
530   peers_left--;
531   if (peers_left == 0)
532     {
533 #if VERBOSE
534       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
535                   "All %d daemons started, now creating topology!\n",
536                   num_peers);
537 #endif
538       GNUNET_SCHEDULER_cancel (sched, die_task);
539       /* Set up task in case topology creation doesn't finish
540        * within a reasonable amount of time */
541       die_task = GNUNET_SCHEDULER_add_delayed (sched,
542                                                GNUNET_TIME_relative_multiply
543                                                (GNUNET_TIME_UNIT_MINUTES, 5),
544                                                &end_badly, NULL);
545       create_topology ();
546       ok = 0;
547     }
548 }
549
550
551 static void
552 run (void *cls,
553      struct GNUNET_SCHEDULER_Handle *s,
554      char *const *args,
555      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
556 {
557   sched = s;
558   ok = 1;
559
560   dotOutFile = fopen (dotOutFileName, "w");
561   if (dotOutFile != NULL)
562     {
563       fprintf (dotOutFile, "strict graph G {\n");
564     }
565
566 #if VERBOSE
567   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
568               "Starting daemons based on config file %s\n", cfgfile);
569 #endif
570   if (GNUNET_SYSERR ==
571       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers",
572                                              &num_peers))
573     num_peers = DEFAULT_NUM_PEERS;
574
575   main_cfg = cfg;
576
577   peers_left = num_peers;
578
579   /* Set up a task to end testing if peer start fails */
580   die_task = GNUNET_SCHEDULER_add_delayed (sched,
581                                            GNUNET_TIME_relative_multiply
582                                            (GNUNET_TIME_UNIT_MINUTES, 5),
583                                            &end_badly, NULL);
584
585   pg = GNUNET_TESTING_daemons_start (sched, cfg,
586                                      peers_left, &my_cb, NULL,
587                                      &topology_callback, NULL, NULL);
588
589 }
590
591 static int
592 check ()
593 {
594   char *binary_name;
595   char *config_file_name;
596   GNUNET_asprintf(&binary_name, "test-testing-topology-%s", topology_string);
597   GNUNET_asprintf(&config_file_name, "test_testing_data_topology_%s.conf", topology_string);
598   char *const argv[] = {binary_name,
599     "-c",
600     config_file_name,
601 #if VERBOSE
602     "-L", "DEBUG",
603 #endif
604     NULL
605   };
606   struct GNUNET_GETOPT_CommandLineOption options[] = {
607     GNUNET_GETOPT_OPTION_END
608   };
609   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
610                       argv, binary_name, "nohelp",
611                       options, &run, &ok);
612   GNUNET_free(binary_name);
613   GNUNET_free(config_file_name);
614   return ok;
615 }
616
617 int
618 main (int argc, char *argv[])
619 {
620   int ret;
621   char *binary_start_pos;
622   char *our_binary_name;
623
624   binary_start_pos = rindex(argv[0], '/');
625   topology_string = strstr (binary_start_pos,
626                             "_topology");
627   GNUNET_assert (topology_string != NULL);
628   topology_string++;
629   topology_string = strstr (topology_string, "_");
630   GNUNET_assert (topology_string != NULL);
631   topology_string++;
632
633   GNUNET_asprintf(&our_binary_name, "test-testing-topology_%s", topology_string);
634   GNUNET_log_setup (our_binary_name,
635 #if VERBOSE
636                     "DEBUG",
637 #else
638                     "WARNING",
639 #endif
640                     NULL);
641   ret = check ();
642
643   /**
644    * Need to remove base directory, subdirectories taken care
645    * of by the testing framework.
646    */
647   GNUNET_DISK_directory_remove ("/tmp/test-gnunet-testing");
648   GNUNET_free(our_binary_name);
649   return ret;
650 }
651
652 /* end of test_testing_group.c */