468ee5b17a9121da6a6e70b20fb452d0b8552fa6
[oweals/gnunet.git] / src / hostlist / test_gnunet_daemon_hostlist_learning.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 hostlist/test_gnunet_daemon_hostlist.c
22  * @brief test for gnunet_daemon_hostslist.c
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet_util_lib.h"
27 #include "gnunet_arm_service.h"
28 #include "gnunet_core_service.h"
29 #include "gnunet_transport_service.h"
30 #include "gnunet_resolver_service.h"
31 #include "gnunet_statistics_service.h"
32
33 #define VERBOSE GNUNET_YES
34
35 #define START_ARM GNUNET_YES
36 #define MAX_URL_LEN 1000
37
38 /**
39  * How long until wait until testcases fails
40  */
41 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 20)
42 #define CHECK_INTERVALL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
43
44 static int timeout;
45 static int adv_arrived;
46 static int adv_sent;
47 static int learned_hostlist_saved;
48 static int learned_hostlist_downloaded;
49
50 static char * current_adv_uri;
51
52 static struct GNUNET_SCHEDULER_Handle *sched;
53
54 static GNUNET_SCHEDULER_TaskIdentifier timeout_task;
55 static GNUNET_SCHEDULER_TaskIdentifier check_task;
56     
57 struct PeerContext
58 {
59   struct GNUNET_CONFIGURATION_Handle *cfg;
60   struct GNUNET_TRANSPORT_Handle *th;
61   struct GNUNET_MessageHeader *hello;
62   struct GNUNET_ARM_Handle *arm;
63   struct GNUNET_CORE_Handle *core;
64   struct GNUNET_STATISTICS_Handle *stats;
65 #if START_ARM
66   pid_t arm_pid;
67 #endif
68 };
69
70 static struct PeerContext adv_peer;
71
72 static struct PeerContext learn_peer;
73
74 static void
75 clean_up (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
76 {
77   if (adv_peer.th != NULL)
78   {
79     GNUNET_TRANSPORT_disconnect (adv_peer.th);
80     adv_peer.th = NULL;
81   }
82   if (learn_peer.th != NULL)
83   {
84     GNUNET_TRANSPORT_disconnect (learn_peer.th);
85     learn_peer.th = NULL;
86   }
87   if (adv_peer.core != NULL)
88   {
89     GNUNET_CORE_disconnect (adv_peer.core);
90     adv_peer.core = NULL;
91   }
92   if (learn_peer.core != NULL)
93   {
94     GNUNET_CORE_disconnect (learn_peer.core);
95     learn_peer.core = NULL;
96   }
97   GNUNET_SCHEDULER_shutdown (sched);
98 }
99
100 static void shutdown_testcase()
101 {
102   if (timeout_task != GNUNET_SCHEDULER_NO_TASK)
103   {
104     GNUNET_SCHEDULER_cancel (sched,
105                              timeout_task);
106     timeout_task = GNUNET_SCHEDULER_NO_TASK;
107   }
108   if (check_task != GNUNET_SCHEDULER_NO_TASK)
109   {
110     GNUNET_SCHEDULER_cancel (sched,
111         check_task);
112     check_task = GNUNET_SCHEDULER_NO_TASK;
113   }
114   GNUNET_free (current_adv_uri);
115   GNUNET_SCHEDULER_add_now (sched,
116                             &clean_up, NULL);
117 }
118
119 /**
120  * Timeout, give up.
121  */
122 static void
123 timeout_error (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
124 {
125   timeout_task = GNUNET_SCHEDULER_NO_TASK;
126   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
127               "Timeout while executing testcase, test failed.\n");
128   timeout = GNUNET_YES;
129   clean_up (NULL, tc);
130 }
131
132 static int
133 process_downloads (void *cls,
134               const char *subsystem,
135               const char *name,
136               uint64_t value,
137               int is_persistent)
138 {
139   if ( (value == 1) && (learned_hostlist_downloaded == GNUNET_NO) )
140   {
141     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
142                 _("Client has successfully downloaded advertised URI \n"));
143     learned_hostlist_downloaded = GNUNET_YES;
144   }
145   if ( GNUNET_NO != learned_hostlist_downloaded )
146     shutdown_testcase();
147   return GNUNET_OK;
148 }
149
150 static int
151 process_uris_recv (void *cls,
152               const char *subsystem,
153               const char *name,
154               uint64_t value,
155               int is_persistent)
156 {
157   if ( (value == 1) && (learned_hostlist_saved == GNUNET_NO))
158   {
159     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
160                 _("Client has successfully saved advertised URI \n"));
161     learned_hostlist_saved = GNUNET_YES;
162   }
163   return GNUNET_OK;
164 }
165
166 static int
167 process_adv_sent (void *cls,
168               const char *subsystem,
169               const char *name,
170               uint64_t value,
171               int is_persistent)
172 {
173   if ( (value == 1) && (adv_sent == GNUNET_NO))
174   {
175     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
176                 _("Server has successfully sent advertisement\n"));
177     adv_sent = GNUNET_YES;
178   }
179   return GNUNET_OK;
180 }
181
182 /**
183  * Check the server statistics regularly
184  */
185 static void
186 check_statistics (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
187 {
188   char *stat;
189   GNUNET_asprintf (&stat,
190                    gettext_noop("Advertised URI `%s' downloaded"),
191                    current_adv_uri);
192   GNUNET_STATISTICS_get (learn_peer.stats,
193                          "hostlist",
194                          stat,
195                          GNUNET_TIME_UNIT_MINUTES,
196                          NULL,
197                          &process_downloads,
198                          NULL);
199   GNUNET_free (stat);
200   GNUNET_STATISTICS_get (learn_peer.stats,
201                          "hostlist",
202                          gettext_noop("# advertised hostlist URIs"),
203                          GNUNET_TIME_UNIT_MINUTES,
204                          NULL,
205                          &process_uris_recv,
206                          NULL);
207   GNUNET_STATISTICS_get (adv_peer.stats,
208                          "hostlist",
209                          gettext_noop("# hostlist advertisements send"),
210                          GNUNET_TIME_UNIT_MINUTES,
211                          NULL,
212                          &process_adv_sent,
213                          NULL);
214   check_task = GNUNET_SCHEDULER_add_delayed (sched,
215                                 CHECK_INTERVALL,
216                                 &check_statistics,
217                                 NULL);
218 }
219
220 /**
221  * Core handler for p2p hostlist advertisements
222  */
223 static int ad_arrive_handler (void *cls,
224                              const struct GNUNET_PeerIdentity * peer,
225                              const struct GNUNET_MessageHeader * message,
226                              struct GNUNET_TIME_Relative latency,
227                              uint32_t distance)
228 {
229   char *hostname;
230   char *expected_uri = GNUNET_malloc (MAX_URL_LEN);
231
232   unsigned long long port;
233   size_t size;
234   const struct GNUNET_MessageHeader * incoming;
235
236   if (-1 == GNUNET_CONFIGURATION_get_value_number (adv_peer.cfg,
237                                                    "HOSTLIST",
238                                                    "HTTPPORT",
239                                                    &port))
240     {
241     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
242                 "Could not read advertising server's configuration\n" );
243     return GNUNET_SYSERR;
244     }
245   hostname = GNUNET_RESOLVER_local_hostname_get ();
246   if (NULL != hostname)
247     {
248       size = strlen (hostname);
249       if (size + 15 > MAX_URL_LEN)
250         {
251           GNUNET_break (0);
252         }
253       else
254         {
255           GNUNET_asprintf (&expected_uri,
256                            "http://%s:%u/",
257                            hostname,
258                            (unsigned int) port);
259         }
260     }
261
262   incoming = (const struct GNUNET_MessageHeader *) message;
263   current_adv_uri = strdup ((char*) &incoming[1]);
264   if ( 0 == strcmp( expected_uri, current_adv_uri ) )
265   {
266     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
267                 "Recieved hostlist advertisement with URI `%s'as expected\n", current_adv_uri);
268     adv_arrived = GNUNET_YES;
269   }
270   else
271     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
272                 "Expected URI `%s' and recieved URI `%s' differ\n", expected_uri, current_adv_uri);
273   GNUNET_free ( expected_uri );
274   GNUNET_free ( hostname );
275   return GNUNET_OK;
276 }
277
278 /**
279  * List of handlers if we are learning.
280  */
281 static struct GNUNET_CORE_MessageHandler learn_handlers[] = {
282   { &ad_arrive_handler, GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT, 0},
283   { NULL, 0, 0 }
284 };
285
286 static void
287 setup_learn_peer (struct PeerContext *p, const char *cfgname)
288 {
289   char * filename;
290   unsigned int result;
291   p->cfg = GNUNET_CONFIGURATION_create ();
292 #if START_ARM
293   p->arm_pid = GNUNET_OS_start_process (NULL, NULL, "gnunet-service-arm",
294                                         "gnunet-service-arm",
295 #if VERBOSE
296                                         "-L", "DEBUG",
297 #endif
298                                         "-c", cfgname, NULL);
299 #endif
300   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
301   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (p->cfg,
302                                                           "HOSTLIST",
303                                                           "HOSTLISTFILE",
304                                                           &filename))
305   {
306   if ( GNUNET_YES == GNUNET_DISK_file_test (filename) )
307     {
308       result = remove (filename);
309       if (result == 0)
310       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
311             _("Hostlist file `%s' was removed\n"),filename);
312     }
313   }
314   if ( NULL != filename)  GNUNET_free ( filename );
315
316   GNUNET_ARM_start_services (p->cfg, sched, "core", NULL);
317
318   p->core = GNUNET_CORE_connect (sched, p->cfg,
319                               GNUNET_TIME_UNIT_FOREVER_REL,
320                               NULL,
321                               NULL,
322                               NULL, NULL,
323                               NULL, GNUNET_NO,
324                               NULL, GNUNET_NO,
325                               learn_handlers );
326   GNUNET_assert ( NULL != p->core );
327   p->stats = GNUNET_STATISTICS_create (sched, "hostlist", p->cfg);
328   GNUNET_assert ( NULL != p->stats );
329 }
330
331
332 static void
333 setup_adv_peer (struct PeerContext *p, const char *cfgname)
334 {
335   p->cfg = GNUNET_CONFIGURATION_create ();
336 #if START_ARM
337   p->arm_pid = GNUNET_OS_start_process (NULL, NULL, "gnunet-service-arm",
338                                         "gnunet-service-arm",
339 #if VERBOSE
340                                         "-L", "DEBUG",
341 #endif
342                                         "-c", cfgname, NULL);
343 #endif
344   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
345   GNUNET_ARM_start_services (p->cfg, sched, "core", NULL);
346   p->stats = GNUNET_STATISTICS_create (sched, "hostlist", p->cfg);
347   GNUNET_assert ( NULL != p->stats );
348 }
349
350
351
352 static void
353 waitpid_task (void *cls, 
354               const struct GNUNET_SCHEDULER_TaskContext *tc)
355 {
356   struct PeerContext *p = cls;
357
358 #if START_ARM 
359   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
360               "Killing ARM process.\n");
361   if (0 != PLIBC_KILL (p->arm_pid, SIGTERM))
362     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
363   if (GNUNET_OS_process_wait(p->arm_pid) != GNUNET_OK)
364     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
365   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
366               "ARM process %u stopped\n", p->arm_pid);
367 #endif
368   GNUNET_CONFIGURATION_destroy (p->cfg);
369 }
370
371
372 static void
373 stop_cb (void *cls, 
374          int success)
375 {
376   struct PeerContext *p = cls;
377
378   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
379               success
380               ? "ARM stopped core service\n"
381               : "ARM failed to stop core service\n");
382   GNUNET_ARM_disconnect (p->arm);
383   p->arm = NULL;
384   /* make sure this runs after all other tasks are done */
385   GNUNET_SCHEDULER_add_delayed (sched,
386                                 GNUNET_TIME_UNIT_SECONDS,
387                                 &waitpid_task, p);
388 }
389
390
391 static void
392 stop_arm (struct PeerContext *p)
393 {
394   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
395               "Asking ARM to stop core service\n");
396   p->arm = GNUNET_ARM_connect (p->cfg, sched, NULL);
397   GNUNET_ARM_stop_service (p->arm, "core", GNUNET_TIME_UNIT_SECONDS,
398                            &stop_cb, p);
399 }
400
401
402 /**
403  * Try again to connect to transport service.
404  */
405 static void
406 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
407 {
408   stop_arm (&adv_peer);
409   stop_arm (&learn_peer);
410 }
411
412
413 static void
414 run (void *cls,
415      struct GNUNET_SCHEDULER_Handle *s,
416      char *const *args,
417      const char *cfgfile, 
418      const struct GNUNET_CONFIGURATION_Handle *cfg)
419 {
420   timeout = GNUNET_NO;
421   adv_arrived = GNUNET_NO;
422   adv_sent =GNUNET_NO;
423   learned_hostlist_downloaded = GNUNET_NO;
424   sched = s;
425   timeout_task = GNUNET_SCHEDULER_add_delayed (sched,
426                                                TIMEOUT,
427                                                &timeout_error,
428                                                NULL);
429   check_task = GNUNET_SCHEDULER_add_delayed (sched,
430                                 CHECK_INTERVALL,
431                                 &check_statistics,
432                                 NULL);
433   GNUNET_SCHEDULER_add_delayed (sched,
434                                 GNUNET_TIME_UNIT_FOREVER_REL,
435                                 &shutdown_task,
436                                 NULL);
437   setup_adv_peer (&adv_peer, "test_learning_adv_peer.conf");
438   setup_learn_peer (&learn_peer, "test_learning_learn_peer.conf");
439 }
440
441
442 static int
443 check ()
444 {
445   char *const argv[] = { "test-gnunet-daemon-hostlist",
446     "-c", "learning_data.conf",
447 #if VERBOSE
448     "-L", "DEBUG",
449 #endif
450     NULL
451   };
452   struct GNUNET_GETOPT_CommandLineOption options[] = {
453     GNUNET_GETOPT_OPTION_END
454   };
455
456   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
457                       argv, "test-gnunet-daemon-hostlist",
458                       "nohelp", options, &run, NULL);
459
460   if (timeout == GNUNET_YES)
461   {
462     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
463                 "Testcase could not set up two communicating peers, timeout\n");
464     return GNUNET_YES;
465   }
466   if (adv_arrived == GNUNET_NO)
467   {
468     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
469                 "Learning peer did not recieve advertisement from server\n");
470     return GNUNET_YES;
471   }
472   if ( learned_hostlist_saved == GNUNET_NO )
473     {
474       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
475                   "Advertisement hostlist was not saved in datastore\n");
476       return GNUNET_YES;
477     }
478   if (learned_hostlist_downloaded == GNUNET_NO)
479   {
480     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
481                 "Advertisement hostlist could not be downloaded from server\n");
482     return GNUNET_YES;
483   }
484
485   if (adv_sent == GNUNET_NO)
486   {
487     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
488                 "Advertisement was not sent from server to client\n");
489     return GNUNET_YES;
490   }
491   return GNUNET_NO;
492 }
493
494 int
495 main (int argc, char *argv[])
496 {
497   
498   int ret;
499
500   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-hostlist-peer-1");
501   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-hostlist-peer-2");
502   GNUNET_log_setup ("test-gnunet-daemon-hostlist",
503 #if VERBOSE
504                     "DEBUG",
505 #else
506                     "WARNING",
507 #endif
508                     NULL);
509   ret = check ();
510   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-hostlist-peer-1");
511   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-hostlist-peer-2");
512   return ret; 
513 }
514
515 /* end of test_gnunet_daemon_hostlist.c */