- make sure -1LL is consistently used everywhere, document
[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 3
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, 300)
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 regex_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                                           "23456789ABC",
126
127                                           "0123456789A*BC",
128
129                                           "0*123456789ABC*"};
130
131
132 /**
133  * Service strings to look for.
134  */
135 static char *strings[MESH_REGEX_PEERS] = {"1123456789ABC",
136
137                                           "0123456789AABC",
138
139                                           "00123456789ABCCCC"};
140
141 /**
142  * Check whether peers successfully shut down.
143  *
144  * @param cls Closure (unused).
145  * @param emsg Error message, NULL on success.
146  */
147 static void
148 shutdown_callback (void *cls, const char *emsg)
149 {
150   unsigned int i;
151
152   if (emsg != NULL)
153   {
154     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
155                 "test: Shutdown of peers failed! (%s)\n", emsg);
156     for (i = 0; i < MESH_REGEX_PEERS; i++)
157       ok[i] = GNUNET_NO;
158   }
159 #if VERBOSE
160   else
161   {
162     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
163                 "test: All peers successfully shut down!\n");
164   }
165 #endif
166   GNUNET_CONFIGURATION_destroy (testing_cfg);
167 }
168
169
170 /**
171  * Task to run for shutdown: stops peers, ends test.
172  *
173  * @param cls Closure (not used).
174  * @param tc TaskContext.
175  *
176  */
177 static void
178 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
179 {
180 #if VERBOSE
181   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Ending test.\n");
182 #endif
183   shutdown_handle = GNUNET_SCHEDULER_NO_TASK;
184   GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
185 }
186
187
188 /**
189  * Ends test: Disconnects peers and calls shutdown.
190  * @param cls Closure (not used).
191  * @param tc TaskContext. 
192  */
193 static void
194 disconnect_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
195 {
196   unsigned int i;
197
198   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: disconnecting peers\n");
199
200   for (i = 0; i < MESH_REGEX_PEERS; i++)
201   {
202     GNUNET_MESH_tunnel_destroy (t[i]);
203     GNUNET_MESH_disconnect (h2[i]);
204   }
205   GNUNET_MESH_disconnect (h1);
206   if (GNUNET_SCHEDULER_NO_TASK != shutdown_handle)
207   {
208     GNUNET_SCHEDULER_cancel (shutdown_handle);
209   }
210   shutdown_handle = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
211 }
212
213 /**
214  * Function called whenever an inbound tunnel is destroyed.  Should clean up
215  * any associated state.
216  *
217  * @param cls closure (set from GNUNET_MESH_connect)
218  * @param tunnel connection to the other end (henceforth invalid)
219  * @param tunnel_ctx place where local state associated
220  *                   with the tunnel is stored
221  */
222 static void
223 tunnel_cleaner (void *cls, const struct GNUNET_MESH_Tunnel *tunnel,
224                 void *tunnel_ctx)
225 {
226   long i = (long) cls;
227
228   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
229               "Incoming tunnel disconnected at peer %d\n",
230               i);
231   return;
232 }
233
234
235 /**
236  * Method called whenever a tunnel falls apart.
237  *
238  * @param cls closure
239  * @param peer peer identity the tunnel stopped working with
240  */
241 static void
242 dh (void *cls, const struct GNUNET_PeerIdentity *peer)
243 {
244   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
245               "peer %s disconnected\n",
246               GNUNET_i2s (peer));
247   return;
248 }
249
250 /**
251  * Function called to notify a client about the connection
252  * begin ready to queue more data.  "buf" will be
253  * NULL and "size" zero if the connection was closed for
254  * writing in the meantime.
255  *
256  * @param cls closure
257  * @param size number of bytes available in buf
258  * @param buf where the callee should write the message
259  * @return number of bytes written to buf
260  */
261 static size_t
262 data_ready (void *cls, size_t size, void *buf)
263 {
264   struct GNUNET_MessageHeader *m = buf;
265
266   if (NULL == buf || size < sizeof(struct GNUNET_MessageHeader))
267     return 0;
268   m->type = htons (1);
269   m->size = htons (sizeof(struct GNUNET_MessageHeader));
270   return sizeof(struct GNUNET_MessageHeader);
271 }
272
273 /**
274  * Method called whenever a peer connects to a tunnel.
275  *
276  * @param cls closure
277  * @param peer peer identity the tunnel was created to, NULL on timeout
278  * @param atsi performance data for the connection
279  */
280 static void
281 ch (void *cls, const struct GNUNET_PeerIdentity *peer,
282     const struct GNUNET_ATS_Information *atsi)
283 {
284   long i = (long) cls;
285
286   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
287               "Peer connected: %s\n",
288               GNUNET_i2s (peer));
289   regex_peers++;
290  
291   GNUNET_MESH_notify_transmit_ready(t[i], GNUNET_NO,
292                                     GNUNET_TIME_UNIT_FOREVER_REL,
293                                     peer,
294                                     sizeof(struct GNUNET_MessageHeader),
295                                     &data_ready, NULL);
296 }
297
298 /**
299  * Method called whenever another peer has added us to a tunnel
300  * the other peer initiated.
301  *
302  * @param cls closure
303  * @param tunnel new handle to the tunnel
304  * @param initiator peer that started the tunnel
305  * @param atsi performance information for the tunnel
306  * @return initial tunnel context for the tunnel
307  *         (can be NULL -- that's not an error)
308  */
309 static void *
310 incoming_tunnel (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
311                  const struct GNUNET_PeerIdentity *initiator,
312                  const struct GNUNET_ATS_Information *atsi)
313 {
314   long i = (long) cls;
315   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
316               "Incoming tunnel from %s to peer %d\n",
317               GNUNET_i2s (initiator), (long) cls);
318   if (i > 1L && i <= 1L + MESH_REGEX_PEERS)
319   {
320     incoming_t[i - 2] = tunnel;
321     ok[i - 2] = GNUNET_OK;
322   }
323   else
324   {
325     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
326                 "Incoming tunnel for unknown client %lu\n", (long) cls);
327   }
328   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
329   {
330     GNUNET_SCHEDULER_cancel (disconnect_task);
331     disconnect_task =
332         GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_peers, NULL);
333   }
334   return NULL;
335 }
336
337 /**
338  * Function is called whenever a message is received.
339  *
340  * @param cls closure (set from GNUNET_MESH_connect)
341  * @param tunnel connection to the other end
342  * @param tunnel_ctx place to store local state associated with the tunnel
343  * @param sender who sent the message
344  * @param message the actual message
345  * @param atsi performance data for the connection
346  * @return GNUNET_OK to keep the connection open,
347  *         GNUNET_SYSERR to close it (signal serious error)
348  */
349 int
350 data_callback (void *cls, struct GNUNET_MESH_Tunnel *tunnel, void **tunnel_ctx,
351                const struct GNUNET_PeerIdentity *sender,
352                const struct GNUNET_MessageHeader *message,
353                const struct GNUNET_ATS_Information *atsi)
354 {
355   int i;
356   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
357               "test: GOT DATA!\n");
358   for (i = 0; i < MESH_REGEX_PEERS; i++)
359   {
360     if (GNUNET_OK != ok[i]) {
361       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
362               "test: %u DATA MISSING!\n", i);
363       return GNUNET_OK;
364     }
365   }
366   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
367               "test: EVERYONE GOT DATA, FINISHING!\n");
368   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
369   {
370     GNUNET_SCHEDULER_cancel (disconnect_task);
371     disconnect_task =
372         GNUNET_SCHEDULER_add_now (&disconnect_peers, NULL);
373   } 
374   return GNUNET_OK;
375 }
376
377 /**
378  * Handlers, for diverse services
379  */
380 static struct GNUNET_MESH_MessageHandler handlers[] = {
381   {&data_callback, 1, sizeof (struct GNUNET_MessageHeader)},
382   {NULL, 0, 0}
383 };
384
385
386 /**
387  * peergroup_ready: start test when all peers are connected
388  * @param cls closure
389  * @param emsg error message
390  */
391 static void
392 peergroup_ready (void *cls, const char *emsg)
393 {
394   GNUNET_MESH_ApplicationType app;
395   struct GNUNET_TESTING_Daemon *d;
396   unsigned int i;
397
398   if (emsg != NULL)
399   {
400     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
401                 "test: Peergroup callback called with error, aborting test!\n");
402     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "test: Error from testing: `%s'\n",
403                 emsg);
404     GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
405     return;
406   }
407 #if VERBOSE
408   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
409               "test: Peer Group started successfully!\n");
410   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "test: Have %u connections\n",
411               total_connections);
412 #endif
413
414   peers_running = GNUNET_TESTING_daemons_running (pg);
415   if (0 < failed_connections)
416   {
417     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "test: %u connections have FAILED!\n",
418                 failed_connections);
419     disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_peers, NULL);
420     return;
421   }
422   disconnect_task =
423     GNUNET_SCHEDULER_add_delayed (TIMEOUT, &disconnect_peers, NULL);
424   d = GNUNET_TESTING_daemon_get (pg, 1);
425
426   app = (GNUNET_MESH_ApplicationType) 0;
427
428   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
429               "Connect to mesh\n");
430   h1 = GNUNET_MESH_connect (d->cfg, (void *) 1L,
431                             NULL,
432                             NULL,
433                             handlers,
434                             &app);
435   regex_peers = 0;
436   for (i = 0; i < MESH_REGEX_PEERS; i++)
437   {
438     ok[i] = GNUNET_NO;
439     d = GNUNET_TESTING_daemon_get (pg, 10 + i);
440     h2[i] = GNUNET_MESH_connect (d->cfg, (void *) (long) (i + 2),
441                                  &incoming_tunnel,
442                                  &tunnel_cleaner,
443                                  handlers,
444                                  &app);
445     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
446                 "Announce REGEX %u: %s\n", i, regexes[i]);
447     GNUNET_MESH_announce_regex (h2[i], regexes[i]);
448   }
449
450   for (i = 0; i < MESH_REGEX_PEERS; i++)
451   {
452 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
453                 "Create tunnel\n");
454     t[i] = GNUNET_MESH_tunnel_create (h1, NULL, &ch, &dh, (void *) (long) i);
455     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
456                 "Connect by string %s\n", strings[i]);
457     GNUNET_MESH_peer_request_connect_by_string (t[i], strings[i]);
458   }
459   /* connect handler = success, timeout = error */
460   
461 }
462
463
464 /**
465  * Function that will be called whenever two daemons are connected by
466  * the testing library.
467  *
468  * @param cls closure
469  * @param first peer id for first daemon
470  * @param second peer id for the second daemon
471  * @param distance distance between the connected peers
472  * @param first_cfg config for the first daemon
473  * @param second_cfg config for the second daemon
474  * @param first_daemon handle for the first daemon
475  * @param second_daemon handle for the second daemon
476  * @param emsg error message (NULL on success)
477  */
478 static void
479 connect_cb (void *cls, const struct GNUNET_PeerIdentity *first,
480             const struct GNUNET_PeerIdentity *second, uint32_t distance,
481             const struct GNUNET_CONFIGURATION_Handle *first_cfg,
482             const struct GNUNET_CONFIGURATION_Handle *second_cfg,
483             struct GNUNET_TESTING_Daemon *first_daemon,
484             struct GNUNET_TESTING_Daemon *second_daemon, const char *emsg)
485 {
486   if (emsg == NULL)
487   {
488     total_connections++;
489   }
490   else
491   {
492     failed_connections++;
493     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
494                 "test: Problem with new connection (%s)\n", emsg);
495     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test:   (%s)\n", GNUNET_i2s (first));
496     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test:   (%s)\n", GNUNET_i2s (second));
497   }
498 }
499
500
501 /**
502  * run: load configuration options and schedule test to run (start peergroup)
503  * @param cls closure
504  * @param args argv
505  * @param cfgfile configuration file name (can be NULL)
506  * @param cfg configuration handle
507  */
508 static void
509 run (void *cls, char *const *args, const char *cfgfile,
510      const struct GNUNET_CONFIGURATION_Handle *cfg)
511 {
512   struct GNUNET_TESTING_Host *hosts;
513
514   total_connections = 0;
515   failed_connections = 0;
516   testing_cfg = GNUNET_CONFIGURATION_dup (cfg);
517
518   GNUNET_log_setup ("test_mesh_regex",
519 #if VERBOSE
520                     "DEBUG",
521 #else
522                     "WARNING",
523 #endif
524                     NULL);
525
526 #if VERBOSE
527   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Starting daemons.\n");
528   GNUNET_CONFIGURATION_set_value_string (testing_cfg, "testing_old",
529                                          "use_progressbars", "YES");
530 #endif
531
532   if (GNUNET_OK !=
533       GNUNET_CONFIGURATION_get_value_number (testing_cfg, "testing_old",
534                                              "num_peers", &num_peers))
535   {
536     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
537                 "Option TESTING:NUM_PEERS is required!\n");
538     return;
539   }
540
541   hosts = GNUNET_TESTING_hosts_load (testing_cfg);
542
543   pg = GNUNET_TESTING_peergroup_start (testing_cfg, num_peers, TIMEOUT,
544                                        &connect_cb, &peergroup_ready, NULL,
545                                        hosts);
546   GNUNET_assert (pg != NULL);
547   shutdown_handle =
548     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
549                                     &shutdown_task, NULL);
550 }
551
552
553 /**
554  * test_mesh_regex command line options
555  */
556 static struct GNUNET_GETOPT_CommandLineOption options[] = {
557   {'V', "verbose", NULL,
558    gettext_noop ("be verbose (print progress information)"),
559    0, &GNUNET_GETOPT_set_one, &verbose},
560   GNUNET_GETOPT_OPTION_END
561 };
562
563
564 /**
565  * Main: start test
566  */
567 int
568 main (int argc, char *argv[])
569 {
570   char *const argv2[] = {
571     argv[0],
572     "-c",
573     "test_mesh_2dtorus.conf",
574 #if VERBOSE
575     "-L",
576     "DEBUG",
577 #endif
578     NULL
579   };
580   int result;
581   unsigned int i;
582
583   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Start\n");
584
585
586   GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1, argv2,
587                       "test_mesh_regex",
588                       gettext_noop ("Test mesh regex integration."),
589                       options, &run, NULL);
590 #if REMOVE_DIR
591   GNUNET_DISK_directory_remove ("/tmp/test_mesh_2dtorus");
592 #endif
593   result = GNUNET_OK;
594   for (i = 0; i < MESH_REGEX_PEERS; i++)
595   {
596     if (GNUNET_OK != ok[i])
597     {
598       result = GNUNET_SYSERR;
599       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
600                   "COULD NOT CONNECT TO %u: %s!\n",
601                   i, strings[i]);
602     }
603   }
604   if (GNUNET_OK != result || regex_peers != MESH_REGEX_PEERS)
605   {
606     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
607                 "test: FAILED! %u connected peers\n",
608                 regex_peers);
609     return 1;
610   }
611   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "test: success\n");
612   return 0;
613 }
614
615 /* end of test_mesh_regex.c */