code
[oweals/gnunet.git] / src / dht / test_dht_api.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 dht/test_dht_api.c
22  * @brief base test case for dht api
23  *
24  * This test case tests DHT api to DUMMY DHT service communication.
25  *
26  */
27 #include "platform.h"
28 #include "gnunet_common.h"
29 #include "gnunet_hello_lib.h"
30 #include "gnunet_getopt_lib.h"
31 #include "gnunet_os_lib.h"
32 #include "gnunet_program_lib.h"
33 #include "gnunet_scheduler_lib.h"
34 #include "gnunet_dht_service.h"
35 #include "gnunet_hello_lib.h"
36
37 #define VERBOSE GNUNET_NO
38
39 #define VERBOSE_ARM GNUNET_NO
40
41 #define START_ARM GNUNET_YES
42
43 /**
44  * How long until we really give up on a particular testcase portion?
45  */
46 #define TOTAL_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 50)
47
48 /**
49  * How long until we give up on any particular operation (and retry)?
50  */
51 #define BASE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 3)
52
53 #define MTYPE 12345
54
55 struct RetryContext
56 {
57   /**
58    * When to really abort the operation.
59    */
60   struct GNUNET_TIME_Absolute real_timeout;
61
62   /**
63    * What timeout to set for the current attempt (increases)
64    */
65   struct GNUNET_TIME_Relative next_timeout;
66
67   /**
68    * The context of the peer we are dealing with.
69    */
70   struct PeerContext *peer_ctx;
71
72   /**
73    * The task identifier of the retry task, so it can be cancelled.
74    */
75   GNUNET_SCHEDULER_TaskIdentifier retry_task;
76
77 };
78
79 struct PeerContext
80 {
81   struct GNUNET_CONFIGURATION_Handle *cfg;
82   struct GNUNET_DHT_Handle *dht_handle;
83   struct GNUNET_PeerIdentity id;
84   struct GNUNET_DHT_GetHandle *get_handle;
85   struct GNUNET_DHT_FindPeerHandle *find_peer_handle;
86
87 #if START_ARM
88   pid_t arm_pid;
89 #endif
90 };
91
92 static struct PeerContext p1;
93
94 struct RetryContext retry_context;
95
96 static struct GNUNET_SCHEDULER_Handle *sched;
97
98 static int ok;
99
100 GNUNET_SCHEDULER_TaskIdentifier die_task;
101
102 #if VERBOSE
103 #define OKPP do { ok++; fprintf (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0)
104 #else
105 #define OKPP do { ok++; } while (0)
106 #endif
107
108
109 static void
110 end (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
111 {
112   /* do work here */
113   GNUNET_SCHEDULER_cancel (sched, die_task);
114
115   GNUNET_DHT_disconnect (p1.dht_handle);
116
117   die_task = GNUNET_SCHEDULER_NO_TASK;
118
119   if (tc->reason == GNUNET_SCHEDULER_REASON_TIMEOUT)
120     {
121       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
122                   "DHT disconnected, returning FAIL!\n");
123       ok = 365;
124     }
125   else
126     {
127       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
128                   "DHT disconnected, returning success!\n");
129       ok = 0;
130     }
131 }
132
133 static void
134 stop_arm (struct PeerContext *p)
135 {
136 #if START_ARM
137   if (0 != PLIBC_KILL (p->arm_pid, SIGTERM))
138     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
139   GNUNET_OS_process_wait (p->arm_pid);
140 #endif
141   GNUNET_CONFIGURATION_destroy (p->cfg);
142 }
143
144
145 static void
146 end_badly ()
147 {
148   /* do work here */
149 #if VERBOSE
150   fprintf (stderr, "Ending on an unhappy note.\n");
151 #endif
152   if (retry_context.peer_ctx->find_peer_handle != NULL)
153     GNUNET_DHT_find_peer_stop(retry_context.peer_ctx->find_peer_handle, NULL, NULL);
154   if (retry_context.retry_task != GNUNET_SCHEDULER_NO_TASK)
155     GNUNET_SCHEDULER_cancel(sched, retry_context.retry_task);
156   GNUNET_DHT_disconnect (p1.dht_handle);
157
158   ok = 1;
159   return;
160 }
161
162 /**
163  * Signature of the main function of a task.
164  *
165  * @param cls closure
166  * @param tc context information (why was this task triggered now)
167  */
168 void
169 test_find_peer_stop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
170 {
171   struct PeerContext *peer = cls;
172
173   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Called test_find_peer_stop!\n");
174   if (tc->reason == GNUNET_SCHEDULER_REASON_TIMEOUT)
175     GNUNET_SCHEDULER_add_now (sched, &end_badly, NULL);
176
177   GNUNET_assert (peer->dht_handle != NULL);
178
179   GNUNET_DHT_find_peer_stop (peer->find_peer_handle, &end, &p1);
180
181   //GNUNET_SCHEDULER_add_delayed(sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 1), &end, &p1);
182
183 }
184
185
186 /**
187  * Iterator called on each result obtained from a find peer
188  * operation
189  *
190  * @param cls closure (NULL)
191  * @param peer the peer we learned about
192  * @param reply response
193  */
194 void test_find_peer_processor (void *cls,
195                                const struct GNUNET_HELLO_Message *hello)
196 {
197   struct RetryContext *retry_ctx = cls;
198   struct GNUNET_PeerIdentity peer;
199
200   if (GNUNET_OK == GNUNET_HELLO_get_id(hello, &peer))
201     {
202       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
203                   "test_find_peer_processor called (peer `%s'), stopping find peer request!\n", GNUNET_i2s(&peer));
204
205       if (retry_ctx->retry_task != GNUNET_SCHEDULER_NO_TASK)
206         {
207           GNUNET_SCHEDULER_cancel(sched, retry_ctx->retry_task);
208           retry_ctx->retry_task = GNUNET_SCHEDULER_NO_TASK;
209         }
210
211       GNUNET_SCHEDULER_add_continuation (sched, &test_find_peer_stop, &p1,
212                                          GNUNET_SCHEDULER_REASON_PREREQ_DONE);
213     }
214   else
215     {
216       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
217                   "received find peer request, but hello_get_id failed!\n");
218     }
219
220 }
221
222 /**
223  * Retry the find_peer task on timeout. (Forward declaration)
224  *
225  * @param cls closure
226  * @param tc context information (why was this task triggered now?)
227  */
228 void
229 retry_find_peer_stop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
230
231 /**
232  * Retry the find_peer task on timeout.
233  *
234  * @param cls closure
235  * @param tc context information (why was this task triggered now)
236  */
237 void
238 retry_find_peer (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
239 {
240   struct RetryContext *retry_ctx = cls;
241   GNUNET_HashCode hash;
242   memset (&hash, 42, sizeof (GNUNET_HashCode));
243
244   if (GNUNET_TIME_absolute_get_remaining(retry_ctx->real_timeout).value > 0)
245     {
246       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
247                   "test_find_peer timed out, retrying!\n");
248       retry_ctx->next_timeout = GNUNET_TIME_relative_multiply(retry_ctx->next_timeout, 2);
249       retry_ctx->peer_ctx->find_peer_handle =
250           GNUNET_DHT_find_peer_start (retry_ctx->peer_ctx->dht_handle, retry_ctx->next_timeout, 0, &hash,
251                                       &test_find_peer_processor, retry_ctx, NULL, NULL);
252     }
253   else
254     {
255       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
256                   "test_find_peer timed out for good, failing!\n");
257
258       retry_ctx->peer_ctx->find_peer_handle = NULL;
259     }
260
261   if (retry_ctx->peer_ctx->find_peer_handle == NULL)
262     GNUNET_SCHEDULER_add_now (sched, &end_badly, &p1);
263   else
264     retry_ctx->retry_task = GNUNET_SCHEDULER_add_delayed(sched, retry_ctx->next_timeout, &retry_find_peer_stop, retry_ctx);
265 }
266
267 /**
268  * Retry the find_peer task on timeout.
269  *
270  * @param cls closure
271  * @param tc context information (why was this task triggered now?)
272  */
273 void
274 retry_find_peer_stop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
275 {
276   struct RetryContext *retry_ctx = cls;
277   GNUNET_HashCode hash;
278   memset (&hash, 42, sizeof (GNUNET_HashCode));
279
280   if (retry_ctx->peer_ctx->find_peer_handle != NULL)
281     GNUNET_DHT_find_peer_stop(retry_ctx->peer_ctx->find_peer_handle, &retry_find_peer, retry_ctx);
282   else
283     GNUNET_SCHEDULER_add_now (sched, &retry_find_peer, retry_ctx);
284
285 }
286
287 /**
288  * Entry point for test of find_peer functionality.
289  *
290  * @param cls closure
291  * @param tc context information (why was this task triggered now)
292  */
293 void
294 test_find_peer (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
295 {
296   struct PeerContext *peer = cls;
297   GNUNET_HashCode hash;
298   memset (&hash, 42, sizeof (GNUNET_HashCode));
299
300   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Called test_find_peer!\n");
301   GNUNET_assert (peer->dht_handle != NULL);
302
303   retry_context.real_timeout = GNUNET_TIME_relative_to_absolute(TOTAL_TIMEOUT);
304   retry_context.next_timeout = BASE_TIMEOUT;
305   retry_context.peer_ctx = peer;
306
307   peer->find_peer_handle =
308     GNUNET_DHT_find_peer_start (peer->dht_handle, retry_context.next_timeout, 0, &hash,
309                                 &test_find_peer_processor, &retry_context, NULL, NULL);
310
311   if (peer->find_peer_handle == NULL)
312     GNUNET_SCHEDULER_add_now (sched, &end_badly, &p1);
313   else
314     retry_context.retry_task = GNUNET_SCHEDULER_add_delayed(sched, retry_context.next_timeout, &retry_find_peer_stop, &retry_context);
315 }
316
317 /**
318  * Signature of the main function of a task.
319  *
320  * @param cls closure
321  * @param tc context information (why was this task triggered now)
322  */
323 void
324 test_get_stop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
325 {
326   struct PeerContext *peer = cls;
327
328   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Called test_get_stop!\n");
329   if (tc->reason == GNUNET_SCHEDULER_REASON_TIMEOUT)
330     GNUNET_SCHEDULER_add_now (sched, &end_badly, NULL);
331
332   GNUNET_assert (peer->dht_handle != NULL);
333
334   GNUNET_DHT_get_stop (peer->get_handle, &test_find_peer, &p1);
335
336   //GNUNET_SCHEDULER_add_delayed(sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 1), &test_put, &p1);
337
338 }
339
340 void
341 test_get_iterator (void *cls,
342                    struct GNUNET_TIME_Absolute exp,
343                    const GNUNET_HashCode * key,
344                    uint32_t type, uint32_t size, const void *data)
345 {
346   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
347               "test_get_iterator called (we got a result), stopping get request!\n");
348
349   GNUNET_SCHEDULER_add_continuation (sched, &test_get_stop, &p1,
350                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
351 }
352
353 /**
354  * Signature of the main function of a task.
355  *
356  * @param cls closure
357  * @param tc context information (why was this task triggered now)
358  */
359 void
360 test_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
361 {
362   struct PeerContext *peer = cls;
363   GNUNET_HashCode hash;
364   memset (&hash, 42, sizeof (GNUNET_HashCode));
365
366   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Called test_get!\n");
367
368   GNUNET_assert (peer->dht_handle != NULL);
369
370   peer->get_handle =
371     GNUNET_DHT_get_start (peer->dht_handle, TOTAL_TIMEOUT, 42, &hash,
372                           &test_get_iterator, NULL, NULL, NULL);
373
374   if (peer->get_handle == NULL)
375     GNUNET_SCHEDULER_add_now (sched, &end_badly, &p1);
376 }
377
378 /**
379  * Signature of the main function of a task.
380  *
381  * @param cls closure
382  * @param tc context information (why was this task triggered now)
383  */
384 void
385 test_put (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
386 {
387   struct PeerContext *peer = cls;
388   GNUNET_HashCode hash;
389   char *data;
390   size_t data_size = 42;
391   memset (&hash, 42, sizeof (GNUNET_HashCode));
392   data = GNUNET_malloc (data_size);
393   memset (data, 43, data_size);
394   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Called test_put!\n");
395   peer->dht_handle = GNUNET_DHT_connect (sched, peer->cfg, 100);
396
397   GNUNET_assert (peer->dht_handle != NULL);
398
399   GNUNET_DHT_put (peer->dht_handle, &hash, 42, data_size, data,
400                   GNUNET_TIME_relative_to_absolute (TOTAL_TIMEOUT), TOTAL_TIMEOUT,
401                   &test_get, &p1);
402   GNUNET_free(data);
403 }
404
405 static void
406 setup_peer (struct PeerContext *p, const char *cfgname)
407 {
408   p->cfg = GNUNET_CONFIGURATION_create ();
409 #if START_ARM
410   p->arm_pid = GNUNET_OS_start_process (NULL, NULL, "gnunet-service-arm",
411                                         "gnunet-service-arm",
412 #if VERBOSE_ARM
413                                         "-L", "DEBUG",
414 #endif
415                                         "-c", cfgname, NULL);
416 #endif
417   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
418
419 }
420
421 static void
422 run (void *cls,
423      struct GNUNET_SCHEDULER_Handle *s,
424      char *const *args,
425      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
426 {
427   GNUNET_assert (ok == 1);
428   OKPP;
429   sched = s;
430
431   die_task = GNUNET_SCHEDULER_add_delayed (sched,
432                                            GNUNET_TIME_relative_multiply
433                                            (GNUNET_TIME_UNIT_MINUTES, 1),
434                                            &end_badly, NULL);
435
436   setup_peer (&p1, "test_dht_api_peer1.conf");
437
438   GNUNET_SCHEDULER_add_delayed (sched,
439                                 GNUNET_TIME_relative_multiply
440                                 (GNUNET_TIME_UNIT_SECONDS, 1), &test_put,
441                                 &p1);
442 }
443
444 static int
445 check ()
446 {
447
448   char *const argv[] = { "test-dht-api",
449     "-c",
450     "test_dht_api_data.conf",
451 #if VERBOSE
452     "-L", "DEBUG",
453 #endif
454     NULL
455   };
456
457   struct GNUNET_GETOPT_CommandLineOption options[] = {
458     GNUNET_GETOPT_OPTION_END
459   };
460
461   ok = 1;
462   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
463                       argv, "test-dht-api", "nohelp", options, &run, &ok);
464   stop_arm (&p1);
465   return ok;
466 }
467
468
469 int
470 main (int argc, char *argv[])
471 {
472   int ret;
473 #ifdef MINGW
474   return GNUNET_SYSERR;
475 #endif
476
477   GNUNET_log_setup ("test-dht-api",
478 #if VERBOSE
479                     "DEBUG",
480 #else
481                     "WARNING",
482 #endif
483                     NULL);
484   ret = check ();
485
486   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-dht-peer-1");
487
488   return ret;
489 }
490
491 /* end of test_dht_api.c */