test working, but fails
[oweals/gnunet.git] / src / dht / test_dht_twopeer.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 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 dht/test_dht_twopeer.c
22  * @brief base testcase for testing DHT service with
23  *        two running peers
24  */
25 #include "platform.h"
26 #include "gnunet_testing_lib.h"
27 #include "gnunet_core_service.h"
28 #include "gnunet_dht_service.h"
29
30 /* DEFINES */
31 #define MAX_GET_ATTEMPTS 10
32
33 #define TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 5)
34
35 #define DEFAULT_NUM_PEERS 2
36
37 /* Structs */
38
39 struct PeerGetContext
40 {
41   struct GNUNET_PeerIdentity *peer;
42
43   struct GNUNET_DHT_Handle *dht_handle;
44
45   struct GNUNET_DHT_GetHandle *get_handle;
46
47   unsigned int get_attempts;
48
49   GNUNET_SCHEDULER_TaskIdentifier retry_task;
50 };
51
52 /* Globals */
53 static char *test_directory;
54
55 static struct PeerGetContext curr_get_ctx;
56
57 static unsigned int expected_connections;
58
59 static unsigned long long peers_left;
60
61 static struct GNUNET_TESTING_PeerGroup *pg;
62
63 static unsigned long long num_peers;
64
65 static unsigned int total_gets;
66
67 static unsigned int gets_succeeded;
68
69 static unsigned int total_connections;
70
71 static unsigned int failed_connections;
72
73 static GNUNET_SCHEDULER_TaskIdentifier die_task;
74
75 static int ok;
76
77 static struct GNUNET_PeerIdentity peer1id;
78
79 static struct GNUNET_PeerIdentity peer2id;
80
81 static struct GNUNET_DHT_Handle *peer1dht;
82
83 static struct GNUNET_DHT_Handle *peer2dht;
84
85 /**
86  * Check whether peers successfully shut down.
87  */
88 static void
89 shutdown_callback (void *cls, const char *emsg)
90 {
91   if (emsg != NULL)
92   {
93     if (ok == 0)
94       ok = 2;
95   }
96 }
97
98 static void
99 finish_testing (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
100 {
101   GNUNET_assert (pg != NULL);
102   GNUNET_assert (peer1dht != NULL);
103   GNUNET_assert (peer2dht != NULL);
104   GNUNET_DHT_disconnect (peer1dht);
105   GNUNET_DHT_disconnect (peer2dht);
106   GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
107   pg = NULL;
108   ok = 0;
109 }
110
111 static void
112 end_badly_cont (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
113 {
114   if (peer1dht != NULL)
115     GNUNET_DHT_disconnect (peer1dht);
116
117   if (peer2dht != NULL)
118     GNUNET_DHT_disconnect (peer2dht);
119
120   if (pg != NULL)
121   {
122     GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
123     pg = NULL;
124   }
125
126   if (curr_get_ctx.retry_task != GNUNET_SCHEDULER_NO_TASK)
127   {
128     GNUNET_SCHEDULER_cancel (curr_get_ctx.retry_task);
129     curr_get_ctx.retry_task = GNUNET_SCHEDULER_NO_TASK;
130   }
131 }
132
133
134 static void
135 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
136 {
137   const char *emsg = cls;
138
139   FPRINTF (stderr, "Error: %s\n", emsg);
140   if (curr_get_ctx.retry_task != GNUNET_SCHEDULER_NO_TASK)
141   {
142     GNUNET_SCHEDULER_cancel (curr_get_ctx.retry_task);
143     curr_get_ctx.retry_task = GNUNET_SCHEDULER_NO_TASK;
144   }
145   if (curr_get_ctx.get_handle != NULL)
146   {
147     GNUNET_DHT_get_stop (curr_get_ctx.get_handle);
148   }
149
150   GNUNET_SCHEDULER_add_now (&end_badly_cont, NULL);
151   ok = 1;
152 }
153
154
155 /* Forward declaration */
156 static void
157 do_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
158
159 /**
160  * Iterator called on each result obtained for a DHT
161  * operation that expects a reply
162  *
163  * @param cls closure
164  * @param exp when will this value expire
165  * @param key key of the result
166  * @param type type of the result
167  * @param size number of bytes in data
168  * @param data pointer to the result data
169  */
170 static void
171 get_result_iterator (void *cls, struct GNUNET_TIME_Absolute exp,
172                      const struct GNUNET_HashCode * key,
173                      const struct GNUNET_PeerIdentity *get_path,
174                      unsigned int get_path_length,
175                      const struct GNUNET_PeerIdentity *put_path,
176                      unsigned int put_path_length, enum GNUNET_BLOCK_Type type,
177                      size_t size, const void *data)
178 {
179   struct PeerGetContext *get_context = cls;
180
181   if (0 !=
182       memcmp (&get_context->peer->hashPubKey, key, sizeof (struct GNUNET_HashCode)))
183   {
184     FPRINTF (stderr, "%s",  "??\n");
185     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
186                 "Key returned is not the same key as was searched for!\n");
187     GNUNET_SCHEDULER_cancel (die_task);
188     die_task =
189         GNUNET_SCHEDULER_add_now (&end_badly,
190                                   "key mismatch in get response!\n");
191     return;
192   }
193   if (get_context->retry_task != GNUNET_SCHEDULER_NO_TASK)
194   {
195     GNUNET_SCHEDULER_cancel (get_context->retry_task);
196     get_context->retry_task = GNUNET_SCHEDULER_NO_TASK;
197   }
198
199   if (get_context->peer == &peer2id)
200   {
201     get_context->peer = &peer1id;
202     get_context->dht_handle = peer2dht;
203     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
204                 "Received first correct GET request response!\n");
205     GNUNET_DHT_get_stop (get_context->get_handle);
206     GNUNET_SCHEDULER_add_now (&do_get, get_context);
207   }
208   else
209   {
210     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
211                 "Received second correct GET request response!\n");
212     GNUNET_SCHEDULER_cancel (die_task);
213     GNUNET_DHT_get_stop (get_context->get_handle);
214     die_task = GNUNET_SCHEDULER_add_now (&finish_testing, NULL);
215   }
216
217 }
218
219 static void
220 stop_retry_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
221
222
223 static void
224 get_stop_finished (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
225 {
226   struct PeerGetContext *get_context = cls;
227
228   if (get_context->get_attempts >= MAX_GET_ATTEMPTS)
229   {
230     FPRINTF (stderr, "%s",  "?\n");
231     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
232                 "Too many attempts failed, ending test!\n",
233                 get_context->get_attempts);
234     GNUNET_SCHEDULER_cancel (die_task);
235     die_task =
236         GNUNET_SCHEDULER_add_now (&end_badly,
237                                   "GET attempt failed, ending test!\n");
238     return;
239   }
240   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
241               "Get attempt %u failed, retrying request!\n",
242               get_context->get_attempts);
243   FPRINTF (stderr, "%s",  ".");
244   get_context->get_attempts++;
245   get_context->retry_task =
246       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
247                                     (GNUNET_TIME_UNIT_SECONDS, 60),
248                                     &stop_retry_get, get_context);
249   get_context->get_handle =
250       GNUNET_DHT_get_start (get_context->dht_handle,
251                             GNUNET_BLOCK_TYPE_DHT_HELLO,
252                             &get_context->peer->hashPubKey, 1,
253                             GNUNET_DHT_RO_NONE, NULL, 0, &get_result_iterator,
254                             get_context);
255 }
256
257
258 static void
259 stop_retry_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
260 {
261   struct PeerGetContext *get_context = cls;
262
263   get_context->retry_task = GNUNET_SCHEDULER_NO_TASK;
264   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
265               "Get attempt %u failed, canceling request!\n",
266               get_context->get_attempts);
267   GNUNET_DHT_get_stop (get_context->get_handle);
268   get_context->get_handle = NULL;
269   GNUNET_SCHEDULER_add_now (&get_stop_finished, get_context);
270 }
271
272
273 static void
274 do_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
275 {
276   struct PeerGetContext *get_context = cls;
277
278   get_context->retry_task =
279       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
280                                     (GNUNET_TIME_UNIT_SECONDS, 10),
281                                     &stop_retry_get, get_context);
282   get_context->get_handle =
283       GNUNET_DHT_get_start (get_context->dht_handle,
284                             GNUNET_BLOCK_TYPE_DHT_HELLO,
285                             &get_context->peer->hashPubKey, 1,
286                             GNUNET_DHT_RO_FIND_PEER, NULL, 0,
287                             &get_result_iterator, get_context);
288 }
289
290
291 static void
292 topology_callback (void *cls, const struct GNUNET_PeerIdentity *first,
293                    const struct GNUNET_PeerIdentity *second, uint32_t distance,
294                    const struct GNUNET_CONFIGURATION_Handle *first_cfg,
295                    const struct GNUNET_CONFIGURATION_Handle *second_cfg,
296                    struct GNUNET_TESTING_Daemon *first_daemon,
297                    struct GNUNET_TESTING_Daemon *second_daemon,
298                    const char *emsg)
299 {
300   if (emsg == NULL)
301   {
302     total_connections++;
303     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
304                 "connected peer %s to peer %s, distance %u\n",
305                 first_daemon->shortname, second_daemon->shortname, distance);
306   }
307   else
308   {
309     failed_connections++;
310     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
311                 "Failed to connect peer %s to peer %s with error :\n%s\n",
312                 first_daemon->shortname, second_daemon->shortname, emsg);
313   }
314
315   if (total_connections == expected_connections)
316   {
317     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
318                 "Created %d total connections, which is our target number!  Starting next phase of testing.\n",
319                 total_connections);
320     GNUNET_SCHEDULER_cancel (die_task);
321     die_task =
322         GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly,
323                                       "Timeout trying to GET");
324
325     curr_get_ctx.dht_handle = peer1dht;
326     curr_get_ctx.peer = &peer2id;
327     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
328                                   (GNUNET_TIME_UNIT_SECONDS, 2), &do_get,
329                                   &curr_get_ctx);
330   }
331   else if (total_connections + failed_connections == expected_connections)
332   {
333     GNUNET_SCHEDULER_cancel (die_task);
334     die_task =
335         GNUNET_SCHEDULER_add_now (&end_badly,
336                                   "from topology_callback (too many failed connections)");
337   }
338 }
339
340
341 static void
342 connect_topology (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
343 {
344   expected_connections = -1;
345   if ((pg != NULL) && (peers_left == 0))
346     expected_connections =
347         GNUNET_TESTING_connect_topology (pg, GNUNET_TESTING_TOPOLOGY_CLIQUE,
348                                          GNUNET_TESTING_TOPOLOGY_OPTION_ALL,
349                                          0.0, TIMEOUT, 12, NULL, NULL);
350
351   GNUNET_SCHEDULER_cancel (die_task);
352   if (expected_connections == GNUNET_SYSERR)
353     die_task =
354         GNUNET_SCHEDULER_add_now (&end_badly,
355                                   "from connect topology (bad return)");
356   else
357     die_task =
358         GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly,
359                                       "from connect topology (timeout)");
360 }
361
362
363 static void
364 peers_started_callback (void *cls, const struct GNUNET_PeerIdentity *id,
365                         const struct GNUNET_CONFIGURATION_Handle *cfg,
366                         struct GNUNET_TESTING_Daemon *d, const char *emsg)
367 {
368   if (emsg != NULL)
369   {
370     FPRINTF (stderr, "Failed to start daemon: `%s'\n", emsg);
371     return;
372   }
373   GNUNET_assert (id != NULL);
374   if (peers_left == num_peers)
375   {
376     memcpy (&peer1id, id, sizeof (struct GNUNET_PeerIdentity));
377     peer1dht = GNUNET_DHT_connect (cfg, 100);
378     if (peer1dht == NULL)
379     {
380       GNUNET_SCHEDULER_cancel (die_task);
381       die_task =
382           GNUNET_SCHEDULER_add_now (&end_badly, "Failed to get dht handle!\n");
383     }
384   }
385   else
386   {
387     memcpy (&peer2id, id, sizeof (struct GNUNET_PeerIdentity));
388     peer2dht = GNUNET_DHT_connect (cfg, 100);
389     if (peer2dht == NULL)
390     {
391       GNUNET_SCHEDULER_cancel (die_task);
392       die_task =
393           GNUNET_SCHEDULER_add_now (&end_badly, "Failed to get dht handle!\n");
394     }
395   }
396
397
398   peers_left--;
399
400   if (peers_left == 0)
401   {
402     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
403                 "All %d daemons started, now connecting peers!\n", num_peers);
404     GNUNET_SCHEDULER_cancel (die_task);
405     /* Set up task in case topology creation doesn't finish
406      * within a reasonable amount of time */
407     die_task =
408         GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly,
409                                       "from peers_started_callback");
410
411     GNUNET_SCHEDULER_add_now (&connect_topology, NULL);
412     ok = 0;
413   }
414 }
415
416
417 static void
418 run (void *cls, char *const *args, const char *cfgfile,
419      const struct GNUNET_CONFIGURATION_Handle *cfg)
420 {
421
422   if (GNUNET_YES !=
423       GNUNET_CONFIGURATION_get_value_string (cfg, "paths", "servicehome",
424                                              &test_directory))
425   {
426     ok = 404;
427     return;
428   }
429
430   if (GNUNET_SYSERR ==
431       GNUNET_CONFIGURATION_get_value_number (cfg, "testing_old", "num_peers",
432                                              &num_peers))
433     num_peers = DEFAULT_NUM_PEERS;
434
435   peers_left = num_peers;
436   total_gets = num_peers;
437   gets_succeeded = 0;
438   /* Set up a task to end testing if peer start fails */
439   die_task =
440       GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly,
441                                     "didn't start all daemons in reasonable amount of time!!!");
442
443   pg = GNUNET_TESTING_daemons_start (cfg, num_peers, 10, num_peers, TIMEOUT,
444                                      NULL, NULL, &peers_started_callback, NULL,
445                                      &topology_callback, NULL, NULL);
446
447 }
448
449 static int
450 check ()
451 {
452   int ret;
453
454   char *const argv[] = { "test-dht-twopeer",
455     "-c",
456     "test_dht_twopeer_data.conf",
457     NULL
458   };
459   struct GNUNET_GETOPT_CommandLineOption options[] = {
460     GNUNET_GETOPT_OPTION_END
461   };
462   ret =
463       GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv,
464                           "test-dht-twopeer", "nohelp", options, &run, &ok);
465   if (ret != GNUNET_OK)
466   {
467     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
468                 "`test-dht-twopeer': Failed with error code %d\n", ret);
469   }
470   return ok;
471 }
472
473 int
474 main (int argc, char *argv[])
475 {
476   int ret;
477
478   GNUNET_log_setup ("test-dht-twopeer",
479                     "WARNING",
480                     NULL);
481   ret = check ();
482   /**
483    * Need to remove base directory, subdirectories taken care
484    * of by the testing framework.
485    */
486   if (GNUNET_DISK_directory_remove (test_directory) != GNUNET_OK)
487   {
488     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
489                 "Failed to remove testing directory %s\n", test_directory);
490   }
491   return ret;
492 }
493
494 /* end of test_dht_twopeer.c */