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