add support for multi topology testing to makefile (all work for me, but only clique...
[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 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 #if VERBOSE
254   struct GNUNET_PeerIdentity *peer = cls;
255 #endif
256   GNUNET_assert (buf != NULL);
257   m = (struct GNUNET_MessageHeader *) buf;
258   m->type = htons (MTYPE);
259   m->size = htons (sizeof (struct GNUNET_MessageHeader));
260
261   transmit_ready_called++;
262 #if VERBOSE
263   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
264               "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);
265 #endif
266   GNUNET_SCHEDULER_add_now(sched, &schedule_transmission, NULL);
267   return sizeof (struct GNUNET_MessageHeader);
268 }
269
270
271 static struct GNUNET_CORE_MessageHandler handlers[] = {
272   {&process_mtype, MTYPE, sizeof (struct GNUNET_MessageHeader)},
273   {NULL, 0, 0}
274 };
275
276
277
278 static void
279 send_test_messages ()
280 {
281   struct TestMessageContext *pos;
282   struct Connection *conn_pos;
283   die_task = GNUNET_SCHEDULER_add_delayed (sched,
284                                            TIMEOUT,
285                                            &end_badly, "from send test messages");
286
287   int count = 0;
288   int conn_count = 0;
289   pos = test_messages;
290   while (pos != NULL)
291     {
292       conn_pos = global_connections;
293       conn_count = 0;
294       while (conn_pos != NULL)
295         {
296           if (conn_pos->peer == pos->peer1)
297             {
298               pos->peer1handle = conn_pos->server;
299               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
300                                 "Peer matched conn_count %d\n",
301                                 conn_count);
302               break;
303             }
304           conn_count++;
305           conn_pos = conn_pos->next;
306         }
307       GNUNET_assert(pos->peer1handle != NULL);
308
309       /*
310       if (NULL == GNUNET_CORE_notify_transmit_ready (pos->peer1handle,
311                                                    0,
312                                                    TIMEOUT,
313                                                    &pos->peer2->id,
314                                                    sizeof (struct GNUNET_MessageHeader),
315                                                    &transmit_ready, &pos->peer1->id))
316         {
317           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
318                       "RECEIVED NULL when asking core (1) for transmission to peer `%4s'\n",
319                       GNUNET_i2s (&pos->peer2->id));
320         }
321       else
322         {
323           transmit_ready_scheduled++;
324         }
325       */
326       pos = pos->next;
327       count++;
328       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
329                   "Prepared %d messages\n",
330                   count);
331     }
332
333   global_pos = test_messages;
334
335   GNUNET_SCHEDULER_add_now(sched, &schedule_transmission, NULL);
336 }
337
338
339
340 static void
341 init_notify (void *cls,
342              struct GNUNET_CORE_Handle *server,
343              const struct GNUNET_PeerIdentity *my_identity,
344              const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
345 {
346   struct Connection *connection = cls;
347
348 #if VERBOSE
349   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
350               "Core connection to `%4s' established, setting up handles\n",
351               GNUNET_i2s (my_identity));
352 #endif
353   connection->server = server;
354   total_server_connections++;
355
356   if (total_server_connections == num_peers)
357     {
358       GNUNET_SCHEDULER_cancel(sched, die_task);
359       GNUNET_SCHEDULER_add_now(sched, &send_test_messages, NULL);
360     }
361 #if OLD
362   if (context->first_step_done == GNUNET_NO)
363     {
364       context->peer1handle = server;
365 #if VERBOSE
366   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting core to peer 2\n");
367 #endif
368       context->first_step_done = GNUNET_YES;
369       /* connect p2 */
370       GNUNET_CORE_connect (sched,
371                            context->peer2->cfg,
372                            TIMEOUT,
373                            context,
374                            &init_notify,
375                            NULL,
376                            NULL,
377                            NULL,
378                            NULL,
379                            GNUNET_YES,
380                            NULL, GNUNET_YES, handlers);
381
382     }
383   else
384     {
385       context->peer2handle = server;
386 #if VERBOSE
387       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
388                   "Asking core (1) for transmission to peer `%4s'\n",
389                   GNUNET_i2s (&context->peer2->id));
390 #endif
391       transmit_ready_scheduled++;
392       if (NULL == GNUNET_CORE_notify_transmit_ready (context->peer1->server,
393                                                      0,
394                                                      TIMEOUT,
395                                                      &context->peer2->id,
396                                                      sizeof (struct GNUNET_MessageHeader),
397                                                      &transmit_ready, NULL))
398         {
399           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
400                       "RECEIVED NULL when asking core (1) for transmission to peer `%4s'\n",
401                       GNUNET_i2s (&context->peer2->id));
402         }
403     }
404 #endif
405 }
406
407
408 static void
409 setup_handlers ()
410 {
411   int i;
412   struct Connection *new_connection;
413
414   struct GNUNET_TESTING_Daemon *temp_daemon;
415   die_task = GNUNET_SCHEDULER_add_delayed (sched,
416                                            TIMEOUT,
417                                            &end_badly, "from setup_handlers");
418
419
420   /**
421    * Set up a single handler for each peer
422    */
423   for (i = 0; i < num_peers; i++)
424     {
425       new_connection = GNUNET_malloc(sizeof(struct Connection));
426       temp_daemon = GNUNET_TESTING_daemon_get(pg, i);
427       new_connection->peer = temp_daemon;
428       new_connection->server = NULL;
429       new_connection->next = global_connections;
430       global_connections = new_connection;
431
432       GNUNET_CORE_connect (sched,
433                            temp_daemon->cfg,
434                            TIMEOUT,
435                            new_connection,
436                            &init_notify,
437                            NULL,
438                            NULL,
439                            NULL,
440                            NULL,
441                            GNUNET_YES, NULL, GNUNET_YES, handlers);
442     }
443 }
444
445
446
447 void
448 topology_callback (void *cls,
449                    const struct GNUNET_PeerIdentity *first,
450                    const struct GNUNET_PeerIdentity *second,
451                    const struct GNUNET_CONFIGURATION_Handle *first_cfg,
452                    const struct GNUNET_CONFIGURATION_Handle *second_cfg,
453                    struct GNUNET_TESTING_Daemon *first_daemon,
454                    struct GNUNET_TESTING_Daemon *second_daemon,
455                    const char *emsg)
456 {
457   struct TestMessageContext *temp_context;
458   if (emsg == NULL)
459     {
460       total_connections++;
461 #if VERBOSE
462       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "connected peer %s to peer %s\n",
463                first_daemon->shortname,
464                second_daemon->shortname);
465 #endif
466       temp_context = GNUNET_malloc(sizeof(struct TestMessageContext));
467       temp_context->first_step_done = 0;
468       temp_context->peer1handle = NULL;
469       temp_context->peer1 = first_daemon;
470       temp_context->peer2 = second_daemon;
471       temp_context->peer2handle = NULL;
472       temp_context->next = test_messages;
473       test_messages = temp_context;
474
475       expected_messages++;
476       if (dotOutFile != NULL)
477         fprintf(dotOutFile, "\tn%s -- n%s;\n", first_daemon->shortname, second_daemon->shortname);
478     }
479 #if VERBOSE
480   else
481     {
482
483       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to connect peer %s to peer %s with error %s\n",
484                first_daemon->shortname,
485                second_daemon->shortname, emsg);
486     }
487 #endif
488
489   if (total_connections == expected_connections)
490     {
491 #if VERBOSE
492       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
493                   "Created %d total connections, which is our target number!  Calling send messages.\n",
494                   total_connections);
495 #endif
496
497       GNUNET_SCHEDULER_cancel (sched, die_task);
498       die_task = GNUNET_SCHEDULER_add_now (sched, &setup_handlers, NULL);
499     }
500   else
501     {
502 #if VERBOSE
503       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
504                   "Have %d total connections, Need %d\n",
505                   total_connections, expected_connections);
506 #endif
507     }
508 }
509
510
511 static void
512 create_topology ()
513 {
514   expected_connections = -1;
515   if ((pg != NULL) && (peers_left == 0))
516     {
517       /* create_topology will read the topology information from
518          the config already contained in the peer group, so should
519          we have create_topology called from start peers?  I think
520          maybe this way is best so that the client can know both
521          when peers are started, and when they are connected.
522        */
523       expected_connections = GNUNET_TESTING_create_topology (pg);
524 #if VERBOSE
525       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
526                   "Have %d expected connections\n", expected_connections);
527 #endif
528     }
529
530   GNUNET_SCHEDULER_cancel (sched, die_task);
531   if (expected_connections == GNUNET_SYSERR)
532     {
533       die_task = GNUNET_SCHEDULER_add_now (sched,
534                                            &end_badly, NULL);
535     }
536   die_task = GNUNET_SCHEDULER_add_delayed (sched,
537                                            TIMEOUT,
538                                            &end_badly, NULL);
539 }
540
541
542 static void
543 my_cb (void *cls,
544        const struct GNUNET_PeerIdentity *id,
545        const struct GNUNET_CONFIGURATION_Handle *cfg,
546        struct GNUNET_TESTING_Daemon *d, const char *emsg)
547 {
548   GNUNET_assert (id != NULL);
549 #if VERBOSE
550   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Started daemon %d out of %d\n",
551               (num_peers - peers_left) + 1, num_peers);
552 #endif
553   peers_left--;
554   if (peers_left == 0)
555     {
556 #if VERBOSE
557       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
558                   "All %d daemons started, now creating topology!\n",
559                   num_peers);
560 #endif
561       GNUNET_SCHEDULER_cancel (sched, die_task);
562       /* Set up task in case topology creation doesn't finish
563        * within a reasonable amount of time */
564       die_task = GNUNET_SCHEDULER_add_delayed (sched,
565                                                GNUNET_TIME_relative_multiply
566                                                (GNUNET_TIME_UNIT_MINUTES, 5),
567                                                &end_badly, NULL);
568       create_topology ();
569       ok = 0;
570     }
571 }
572
573
574 static void
575 run (void *cls,
576      struct GNUNET_SCHEDULER_Handle *s,
577      char *const *args,
578      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
579 {
580   sched = s;
581   ok = 1;
582
583   dotOutFile = fopen (dotOutFileName, "w");
584   if (dotOutFile != NULL)
585     {
586       fprintf (dotOutFile, "strict graph G {\n");
587     }
588
589 #if VERBOSE
590   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
591               "Starting daemons based on config file %s\n", cfgfile);
592 #endif
593   if (GNUNET_SYSERR ==
594       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers",
595                                              &num_peers))
596     num_peers = DEFAULT_NUM_PEERS;
597
598   main_cfg = cfg;
599
600   peers_left = num_peers;
601
602   /* Set up a task to end testing if peer start fails */
603   die_task = GNUNET_SCHEDULER_add_delayed (sched,
604                                            GNUNET_TIME_relative_multiply
605                                            (GNUNET_TIME_UNIT_MINUTES, 5),
606                                            &end_badly, NULL);
607
608   pg = GNUNET_TESTING_daemons_start (sched, cfg,
609                                      peers_left, &my_cb, NULL,
610                                      &topology_callback, NULL, NULL);
611
612 }
613
614 static int
615 check ()
616 {
617   char *binary_name;
618   char *config_file_name;
619   GNUNET_asprintf(&binary_name, "test-testing-topology-%s", topology_string);
620   GNUNET_asprintf(&config_file_name, "test_testing_data_topology_%s.conf", topology_string);
621   char *const argv[] = {binary_name,
622     "-c",
623     config_file_name,
624 #if VERBOSE
625     "-L", "DEBUG",
626 #endif
627     NULL
628   };
629   struct GNUNET_GETOPT_CommandLineOption options[] = {
630     GNUNET_GETOPT_OPTION_END
631   };
632   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
633                       argv, binary_name, "nohelp",
634                       options, &run, &ok);
635   return ok;
636 }
637
638 int
639 main (int argc, char *argv[])
640 {
641   int ret;
642   char *binary_start_pos;
643   char *our_binary_name;
644   binary_start_pos = rindex(argv[0], '/');
645   if (strstr(binary_start_pos, "test_testing_topology_") == NULL)
646     {
647       return GNUNET_SYSERR;
648     }
649
650   topology_string = &binary_start_pos[23];
651
652   GNUNET_asprintf(&our_binary_name, "test-testing-topology_%s", topology_string);
653   GNUNET_log_setup (our_binary_name,
654 #if VERBOSE
655                     "DEBUG",
656 #else
657                     "WARNING",
658 #endif
659                     NULL);
660   ret = check ();
661   sleep (1);
662   GNUNET_DISK_directory_remove ("/tmp/test-gnunet-testing");
663   return ret;
664 }
665
666 /* end of test_testing_group.c */