-extend regex testcase, add debug info to service
[oweals/gnunet.git] / src / mesh / test_mesh_regex.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012 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 3, 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 mesh/test_mesh_regex.c
22  *
23  * @brief Test for regex announce / by_string connect.
24  * based on the 2dtorus testcase
25  */
26 #include "platform.h"
27 #include "gnunet_testing_lib.h"
28 #include "gnunet_mesh_service.h"
29
30 #define VERBOSE GNUNET_YES
31 #define REMOVE_DIR GNUNET_YES
32 #define MESH_REGEX_PEERS 2
33
34 /**
35  * How long until we give up on connecting the peers?
36  */
37 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
38
39 /**
40  * Time to wait for stuff that should be rather fast
41  */
42 #define SHORT_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
43
44
45 /**
46  * Which strings have been found & connected.
47  */
48 static int ok[MESH_REGEX_PEERS];
49
50 /**
51  * How many connects have happened.
52  */
53 static int connected_peers;
54
55 /**
56  * Be verbose
57  */
58 static int verbose;
59
60 /**
61  * Total number of peers in the test.
62  */
63 static unsigned long long num_peers;
64
65 /**
66  * Global configuration file
67  */
68 static struct GNUNET_CONFIGURATION_Handle *testing_cfg;
69
70 /**
71  * Total number of currently running peers.
72  */
73 static unsigned long long peers_running;
74
75 /**
76  * Total number of successful connections in the whole network.
77  */
78 static unsigned int total_connections;
79
80 /**
81  * Total number of failed connections in the whole network.
82  */
83 static unsigned int failed_connections;
84
85 /**
86  * The currently running peer group.
87  */
88 static struct GNUNET_TESTING_PeerGroup *pg;
89
90 /**
91  * Task called to disconnect peers
92  */
93 static GNUNET_SCHEDULER_TaskIdentifier disconnect_task;
94
95 /**
96  * Task called to shutdown test.
97  */
98 static GNUNET_SCHEDULER_TaskIdentifier shutdown_handle;
99
100 /**
101  * Mesh handle for connecting peer.
102  */
103 static struct GNUNET_MESH_Handle *h1;
104
105 /**
106  * Mesh handle for announcing peers.
107  */
108 static struct GNUNET_MESH_Handle *h2[MESH_REGEX_PEERS];
109
110 /**
111  * Tunnel handles for announcing peer.
112  */
113 static struct GNUNET_MESH_Tunnel *t[MESH_REGEX_PEERS];
114
115 /**
116  * Incoming tunnels for announcing peers.
117  */
118 static struct GNUNET_MESH_Tunnel *incoming_t[MESH_REGEX_PEERS];
119
120 /**
121  * Regular expressions for the announces.
122  */
123 static char *regexes[MESH_REGEX_PEERS] = {"(0|1)"
124                                           "(0|1)"
125                                           "23456789A*BC",
126
127                                           "0123456789ABC"/*,
128
129                                           "0*123456789ABC*"*/};
130 //                                          "(0|1|2|3|4|5|6|7|8|9)"
131
132
133 /**
134  * Service strings to look for.
135  */
136 static char *strings[MESH_REGEX_PEERS] = {"0123456789ABC",
137
138                                           "0123456789ABC"/*,
139
140                                           "000123456789ABCCCC"*/};
141
142 /**
143  * Check whether peers successfully shut down.
144  *
145  * @param cls Closure (unused).
146  * @param emsg Error message, NULL on success.
147  */
148 static void
149 shutdown_callback (void *cls, const char *emsg)
150 {
151   unsigned int i;
152
153   if (emsg != NULL)
154   {
155     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
156                 "test: Shutdown of peers failed! (%s)\n", emsg);
157     for (i = 0; i < MESH_REGEX_PEERS; i++)
158       ok[i] = GNUNET_NO;
159   }
160 #if VERBOSE
161   else
162   {
163     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
164                 "test: All peers successfully shut down!\n");
165   }
166 #endif
167   GNUNET_CONFIGURATION_destroy (testing_cfg);
168 }
169
170
171 /**
172  * Task to run for shutdown: stops peers, ends test.
173  *
174  * @param cls Closure (not used).
175  * @param tc TaskContext.
176  *
177  */
178 static void
179 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
180 {
181 #if VERBOSE
182   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Ending test.\n");
183 #endif
184   shutdown_handle = GNUNET_SCHEDULER_NO_TASK;
185   GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
186 }
187
188
189 /**
190  * Ends test: Disconnects peers and calls shutdown.
191  * @param cls Closure (not used).
192  * @param tc TaskContext. 
193  */
194 static void
195 disconnect_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
196 {
197   unsigned int i;
198
199   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: disconnecting peers\n");
200
201   for (i = 0; i < MESH_REGEX_PEERS; i++)
202   {
203     GNUNET_MESH_tunnel_destroy (t[i]);
204     GNUNET_MESH_disconnect (h2[i]);
205   }
206   GNUNET_MESH_disconnect (h1);
207   if (GNUNET_SCHEDULER_NO_TASK != shutdown_handle)
208   {
209     GNUNET_SCHEDULER_cancel (shutdown_handle);
210   }
211   shutdown_handle = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
212 }
213
214 /**
215  * Function called whenever an inbound tunnel is destroyed.  Should clean up
216  * any associated state.
217  *
218  * @param cls closure (set from GNUNET_MESH_connect)
219  * @param tunnel connection to the other end (henceforth invalid)
220  * @param tunnel_ctx place where local state associated
221  *                   with the tunnel is stored
222  */
223 static void
224 tunnel_cleaner (void *cls, const struct GNUNET_MESH_Tunnel *tunnel,
225                 void *tunnel_ctx)
226 {
227   long i = (long) cls;
228
229   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
230               "Incoming tunnel disconnected at peer %d\n",
231               i);
232   return;
233 }
234
235
236 /**
237  * Method called whenever a tunnel falls apart.
238  *
239  * @param cls closure
240  * @param peer peer identity the tunnel stopped working with
241  */
242 static void
243 dh (void *cls, const struct GNUNET_PeerIdentity *peer)
244 {
245   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
246               "peer %s disconnected\n",
247               GNUNET_i2s (peer));
248   return;
249 }
250
251
252 /**
253  * Method called whenever a peer connects to a tunnel.
254  *
255  * @param cls closure
256  * @param peer peer identity the tunnel was created to, NULL on timeout
257  * @param atsi performance data for the connection
258  */
259 static void
260 ch (void *cls, const struct GNUNET_PeerIdentity *peer,
261     const struct GNUNET_ATS_Information *atsi)
262 {
263   long i = (long) cls;
264
265   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
266               "Peer connected: %s\n",
267               GNUNET_i2s (peer));
268   connected_peers++;
269   GNUNET_assert (i = 1L);
270
271   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
272   {
273     GNUNET_SCHEDULER_cancel (disconnect_task);
274     disconnect_task =
275         GNUNET_SCHEDULER_add_now (&disconnect_peers, NULL);
276   } 
277 }
278
279 /**
280  * Method called whenever another peer has added us to a tunnel
281  * the other peer initiated.
282  *
283  * @param cls closure
284  * @param tunnel new handle to the tunnel
285  * @param initiator peer that started the tunnel
286  * @param atsi performance information for the tunnel
287  * @return initial tunnel context for the tunnel
288  *         (can be NULL -- that's not an error)
289  */
290 static void *
291 incoming_tunnel (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
292                  const struct GNUNET_PeerIdentity *initiator,
293                  const struct GNUNET_ATS_Information *atsi)
294 {
295   long i = (long) cls;
296   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
297               "Incoming tunnel from %s to peer %d\n",
298               GNUNET_i2s (initiator), (long) cls);
299   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
300   if (i > 1L && i <= 1L + MESH_REGEX_PEERS)
301   {
302     incoming_t[i - 1] = tunnel;
303     ok[i - 1] = GNUNET_OK;
304   }
305   else
306   {
307     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
308                 "Incoming tunnel for unknown client %lu\n", (long) cls);
309   }
310   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
311   {
312     GNUNET_SCHEDULER_cancel (disconnect_task);
313     disconnect_task =
314         GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_peers, NULL);
315   }
316   return NULL;
317 }
318
319 /**
320  * Function is called whenever a message is received.
321  *
322  * @param cls closure (set from GNUNET_MESH_connect)
323  * @param tunnel connection to the other end
324  * @param tunnel_ctx place to store local state associated with the tunnel
325  * @param sender who sent the message
326  * @param message the actual message
327  * @param atsi performance data for the connection
328  * @return GNUNET_OK to keep the connection open,
329  *         GNUNET_SYSERR to close it (signal serious error)
330  */
331 int
332 data_callback (void *cls, struct GNUNET_MESH_Tunnel *tunnel, void **tunnel_ctx,
333                const struct GNUNET_PeerIdentity *sender,
334                const struct GNUNET_MessageHeader *message,
335                const struct GNUNET_ATS_Information *atsi)
336 {
337     return GNUNET_OK;
338 }
339
340 /**
341  * Handlers, for diverse services
342  */
343 static struct GNUNET_MESH_MessageHandler handlers[] = {
344   {&data_callback, 1, sizeof (struct GNUNET_MessageHeader)},
345   {NULL, 0, 0}
346 };
347
348
349 /**
350  * peergroup_ready: start test when all peers are connected
351  * @param cls closure
352  * @param emsg error message
353  */
354 static void
355 peergroup_ready (void *cls, const char *emsg)
356 {
357   GNUNET_MESH_ApplicationType app;
358   struct GNUNET_TESTING_Daemon *d;
359   unsigned int i;
360
361   if (emsg != NULL)
362   {
363     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
364                 "test: Peergroup callback called with error, aborting test!\n");
365     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "test: Error from testing: `%s'\n",
366                 emsg);
367     GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
368     return;
369   }
370 #if VERBOSE
371   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
372               "test: Peer Group started successfully!\n");
373   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "test: Have %u connections\n",
374               total_connections);
375 #endif
376
377   peers_running = GNUNET_TESTING_daemons_running (pg);
378   if (0 < failed_connections)
379   {
380     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "test: %u connections have FAILED!\n",
381                 failed_connections);
382     disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_peers, NULL);
383     return;
384   }
385   disconnect_task =
386     GNUNET_SCHEDULER_add_delayed (TIMEOUT, &disconnect_peers, NULL);
387   d = GNUNET_TESTING_daemon_get (pg, 1);
388
389   app = (GNUNET_MESH_ApplicationType) 0;
390
391   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
392               "Connect to mesh\n");
393   h1 = GNUNET_MESH_connect (d->cfg, 5, (void *) 1L,
394                             NULL,
395                             NULL,
396                             handlers,
397                             &app);
398   for (i = 0; i < MESH_REGEX_PEERS; i++)
399   {
400     ok[i] = GNUNET_NO;
401     d = GNUNET_TESTING_daemon_get (pg, 10 + i);
402     h2[i] = GNUNET_MESH_connect (d->cfg, 5, (void *) (long) (i + 1),
403                                  &incoming_tunnel,
404                                  &tunnel_cleaner,
405                                  handlers,
406                                  &app);
407     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
408                 "Announce REGEX %u: %s\n", i, regexes[i]);
409     GNUNET_MESH_announce_regex (h2[i], regexes[i]);
410   }
411
412   for (i = 0; i < MESH_REGEX_PEERS; i++)
413   {
414     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
415                 "Create tunnel\n");
416     t[i] = GNUNET_MESH_tunnel_create (h1, NULL, &ch, &dh, (void *) 1L);
417     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
418                 "Connect by string %s\n", strings[i]);
419     GNUNET_MESH_peer_request_connect_by_string (t[i], strings[i]);
420   }
421   /* connect handler = success, timeout = error */
422   
423 }
424
425
426 /**
427  * Function that will be called whenever two daemons are connected by
428  * the testing library.
429  *
430  * @param cls closure
431  * @param first peer id for first daemon
432  * @param second peer id for the second daemon
433  * @param distance distance between the connected peers
434  * @param first_cfg config for the first daemon
435  * @param second_cfg config for the second daemon
436  * @param first_daemon handle for the first daemon
437  * @param second_daemon handle for the second daemon
438  * @param emsg error message (NULL on success)
439  */
440 static void
441 connect_cb (void *cls, const struct GNUNET_PeerIdentity *first,
442             const struct GNUNET_PeerIdentity *second, uint32_t distance,
443             const struct GNUNET_CONFIGURATION_Handle *first_cfg,
444             const struct GNUNET_CONFIGURATION_Handle *second_cfg,
445             struct GNUNET_TESTING_Daemon *first_daemon,
446             struct GNUNET_TESTING_Daemon *second_daemon, const char *emsg)
447 {
448   if (emsg == NULL)
449   {
450     total_connections++;
451   }
452   else
453   {
454     failed_connections++;
455     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
456                 "test: Problem with new connection (%s)\n", emsg);
457     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test:   (%s)\n", GNUNET_i2s (first));
458     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test:   (%s)\n", GNUNET_i2s (second));
459   }
460 }
461
462
463 /**
464  * run: load configuration options and schedule test to run (start peergroup)
465  * @param cls closure
466  * @param args argv
467  * @param cfgfile configuration file name (can be NULL)
468  * @param cfg configuration handle
469  */
470 static void
471 run (void *cls, char *const *args, const char *cfgfile,
472      const struct GNUNET_CONFIGURATION_Handle *cfg)
473 {
474   struct GNUNET_TESTING_Host *hosts;
475
476   total_connections = 0;
477   failed_connections = 0;
478   testing_cfg = GNUNET_CONFIGURATION_dup (cfg);
479
480   GNUNET_log_setup ("test_mesh_regex",
481 #if VERBOSE
482                     "DEBUG",
483 #else
484                     "WARNING",
485 #endif
486                     NULL);
487
488 #if VERBOSE
489   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Starting daemons.\n");
490   GNUNET_CONFIGURATION_set_value_string (testing_cfg, "testing_old",
491                                          "use_progressbars", "YES");
492 #endif
493
494   if (GNUNET_OK !=
495       GNUNET_CONFIGURATION_get_value_number (testing_cfg, "testing_old",
496                                              "num_peers", &num_peers))
497   {
498     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
499                 "Option TESTING:NUM_PEERS is required!\n");
500     return;
501   }
502
503   hosts = GNUNET_TESTING_hosts_load (testing_cfg);
504
505   pg = GNUNET_TESTING_peergroup_start (testing_cfg, num_peers, TIMEOUT,
506                                        &connect_cb, &peergroup_ready, NULL,
507                                        hosts);
508   GNUNET_assert (pg != NULL);
509   shutdown_handle =
510     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
511                                     &shutdown_task, NULL);
512 }
513
514
515 /**
516  * test_mesh_regex command line options
517  */
518 static struct GNUNET_GETOPT_CommandLineOption options[] = {
519   {'V', "verbose", NULL,
520    gettext_noop ("be verbose (print progress information)"),
521    0, &GNUNET_GETOPT_set_one, &verbose},
522   GNUNET_GETOPT_OPTION_END
523 };
524
525
526 /**
527  * Main: start test
528  */
529 int
530 main (int argc, char *argv[])
531 {
532   char *const argv2[] = {
533     argv[0],
534     "-c",
535     "test_mesh_2dtorus.conf",
536 #if VERBOSE
537     "-L",
538     "DEBUG",
539 #endif
540     NULL
541   };
542   int result;
543   unsigned int i;
544
545   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Start\n");
546
547
548   GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1, argv2,
549                       "test_mesh_regex",
550                       gettext_noop ("Test mesh regex integration."),
551                       options, &run, NULL);
552 #if REMOVE_DIR
553   GNUNET_DISK_directory_remove ("/tmp/test_mesh_2dtorus");
554 #endif
555   result = GNUNET_OK;
556   for (i = 0; i < MESH_REGEX_PEERS; i++)
557   {
558     if (GNUNET_OK != ok[i])
559     {
560       result = GNUNET_SYSERR;
561       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
562                   "COULD NOT CONNECT TO %u: %s!\n",
563                   i, strings[i]);
564     }
565   }
566   if (GNUNET_OK != result || connected_peers != MESH_REGEX_PEERS)
567   {
568     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
569                 "test: FAILED! %u connected peers\n",
570                 connected_peers);
571     return 1;
572   }
573   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: success\n");
574   return 0;
575 }
576
577 /* end of test_mesh_regex.c */