LRN: Make dht test a bit less optimistic about MinGW
[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 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_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, 600)
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   struct GNUNET_OS_Process *arm_proc;
89 #endif
90 };
91
92 static struct PeerContext p1;
93
94 struct RetryContext retry_context;
95
96
97 static int ok;
98
99 GNUNET_SCHEDULER_TaskIdentifier die_task;
100
101 #if VERBOSE
102 #define OKPP do { ok++; fprintf (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0)
103 #else
104 #define OKPP do { ok++; } while (0)
105 #endif
106
107
108 static void
109 end (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
110 {
111   GNUNET_SCHEDULER_cancel (die_task);
112   die_task = GNUNET_SCHEDULER_NO_TASK;
113   GNUNET_DHT_disconnect (p1.dht_handle);
114   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
115               "DHT disconnected, returning success!\n");
116   ok = 0;
117 }
118
119 static void
120 stop_arm (struct PeerContext *p)
121 {
122 #if START_ARM
123   if (0 != GNUNET_OS_process_kill (p->arm_proc, SIGTERM))
124     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
125   GNUNET_OS_process_wait (p->arm_proc);
126   GNUNET_OS_process_close (p->arm_proc);
127   p->arm_proc = NULL;
128 #endif
129   GNUNET_CONFIGURATION_destroy (p->cfg);
130 }
131
132
133 static void
134 end_badly ()
135 {
136   /* do work here */
137 #if VERBOSE
138   fprintf (stderr, "Ending on an unhappy note.\n");
139 #endif
140
141   if ( (retry_context.peer_ctx != NULL) && 
142        (retry_context.peer_ctx->find_peer_handle != NULL) )
143     {
144       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Stopping find peer request!\n");
145       GNUNET_DHT_find_peer_stop(retry_context.peer_ctx->find_peer_handle);
146     }
147   if ( (retry_context.peer_ctx != NULL) && 
148        (retry_context.peer_ctx->get_handle != NULL) )
149     {
150       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Stopping get request!\n");
151       GNUNET_DHT_get_stop (retry_context.peer_ctx->get_handle);
152     }
153   if (retry_context.retry_task != GNUNET_SCHEDULER_NO_TASK)
154     GNUNET_SCHEDULER_cancel(retry_context.retry_task);
155   GNUNET_DHT_disconnect (p1.dht_handle);
156   ok = 1;
157 }
158
159
160 /**
161  * Signature of the main function of a task.
162  *
163  * @param cls closure
164  * @param tc context information (why was this task triggered now)
165  */
166 void
167 test_find_peer_stop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
168 {
169   struct PeerContext *peer = cls;
170
171   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Called test_find_peer_stop!\n");
172   if ( (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT) != 0)
173     {
174       GNUNET_break (0);
175       GNUNET_SCHEDULER_cancel (die_task);
176       GNUNET_SCHEDULER_add_now (&end_badly, NULL);
177       return;
178     }
179
180   GNUNET_assert (peer->dht_handle != NULL);
181
182   GNUNET_DHT_find_peer_stop (peer->find_peer_handle);
183   peer->find_peer_handle = NULL;
184
185 #if HAVE_MALICIOUS
186   GNUNET_DHT_set_malicious_getter (peer->dht_handle, GNUNET_TIME_UNIT_SECONDS, NULL, NULL);
187   GNUNET_DHT_set_malicious_putter (peer->dht_handle, GNUNET_TIME_UNIT_SECONDS, NULL, NULL);
188   GNUNET_DHT_set_malicious_dropper (peer->dht_handle, NULL, NULL);
189 #endif
190   GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 1),
191                                &end, &p1);
192 }
193
194
195 /**
196  * Iterator called on each result obtained from a find peer
197  * operation
198  *
199  * @param cls closure (NULL)
200  * @param peer the peer we learned about
201  * @param reply response
202  */
203 void test_find_peer_processor (void *cls,
204                                const struct GNUNET_HELLO_Message *hello)
205 {
206   struct RetryContext *retry_ctx = cls;
207   struct GNUNET_PeerIdentity peer;
208
209   if (GNUNET_OK == GNUNET_HELLO_get_id(hello, &peer))
210     {
211       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
212                   "test_find_peer_processor called (peer `%s'), stopping find peer request!\n", GNUNET_i2s(&peer));
213
214       if (retry_ctx->retry_task != GNUNET_SCHEDULER_NO_TASK)
215         {
216           GNUNET_SCHEDULER_cancel(retry_ctx->retry_task);
217           retry_ctx->retry_task = GNUNET_SCHEDULER_NO_TASK;
218         }
219
220       GNUNET_SCHEDULER_add_continuation (&test_find_peer_stop, &p1,
221                                          GNUNET_SCHEDULER_REASON_PREREQ_DONE);
222     }
223   else
224     {
225       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
226                   "received find peer request, but hello_get_id failed!\n");
227     }
228
229 }
230
231 /**
232  * Retry the find_peer task on timeout. (Forward declaration)
233  *
234  * @param cls closure
235  * @param tc context information (why was this task triggered now?)
236  */
237 void
238 retry_find_peer_stop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
239
240 /**
241  * Retry the find_peer task on timeout.
242  *
243  * @param cls closure
244  * @param tc context information (why was this task triggered now)
245  */
246 void
247 retry_find_peer (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
248 {
249   struct RetryContext *retry_ctx = cls;
250   GNUNET_HashCode hash;
251   memset (&hash, 42, sizeof (GNUNET_HashCode));
252
253   if (GNUNET_TIME_absolute_get_remaining(retry_ctx->real_timeout).rel_value > 0)
254     {
255       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
256                   "test_find_peer timed out, retrying!\n");
257       retry_ctx->next_timeout = GNUNET_TIME_relative_multiply(retry_ctx->next_timeout, 2);
258       retry_ctx->peer_ctx->find_peer_handle 
259         = GNUNET_DHT_find_peer_start (retry_ctx->peer_ctx->dht_handle, 
260                                       retry_ctx->next_timeout, &hash,
261                                       GNUNET_DHT_RO_NONE,
262                                       &test_find_peer_processor, retry_ctx);
263     }
264   else
265     {
266       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
267                   "test_find_peer timed out for good, failing!\n");
268
269       retry_ctx->peer_ctx->find_peer_handle = NULL;
270     }
271
272   if (retry_ctx->peer_ctx->find_peer_handle == NULL)
273     {
274       GNUNET_break (0);
275       GNUNET_SCHEDULER_cancel (die_task);
276       GNUNET_SCHEDULER_add_now (&end_badly, &p1);
277       return;
278     }
279   retry_ctx->retry_task = GNUNET_SCHEDULER_add_delayed(retry_ctx->next_timeout, &retry_find_peer_stop, retry_ctx);
280 }
281
282 /**
283  * Retry the find_peer task on timeout.
284  *
285  * @param cls closure
286  * @param tc context information (why was this task triggered now?)
287  */
288 void
289 retry_find_peer_stop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
290 {
291   struct RetryContext *retry_ctx = cls;
292   GNUNET_HashCode hash;
293   memset (&hash, 42, sizeof (GNUNET_HashCode));
294
295   if (retry_ctx->peer_ctx->find_peer_handle != NULL)
296     {
297       GNUNET_DHT_find_peer_stop(retry_ctx->peer_ctx->find_peer_handle);
298       retry_ctx->peer_ctx->find_peer_handle = NULL;
299     }  
300   GNUNET_SCHEDULER_add_now (&retry_find_peer, retry_ctx);
301 }
302
303 /**
304  * Entry point for test of find_peer functionality.
305  *
306  * @param cls closure
307  * @param tc context information (why was this task triggered now)
308  */
309 void
310 test_find_peer (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
311 {
312   struct PeerContext *peer = cls;
313   GNUNET_HashCode hash;
314   memset (&hash, 42, sizeof (GNUNET_HashCode));
315
316   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Called test_find_peer!\n");
317   GNUNET_assert (peer->dht_handle != NULL);
318
319   retry_context.real_timeout = GNUNET_TIME_relative_to_absolute(TOTAL_TIMEOUT);
320   retry_context.next_timeout = BASE_TIMEOUT;
321   retry_context.peer_ctx = peer;
322
323   peer->find_peer_handle
324     = GNUNET_DHT_find_peer_start (peer->dht_handle, retry_context.next_timeout, 
325                                   &hash,
326                                   GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
327                                   &test_find_peer_processor, &retry_context);
328
329   if (peer->find_peer_handle == NULL)
330     {
331       GNUNET_break (0);
332       GNUNET_SCHEDULER_cancel (die_task);
333       GNUNET_SCHEDULER_add_now (&end_badly, &p1);
334       return;
335     }
336   retry_context.retry_task = GNUNET_SCHEDULER_add_delayed(retry_context.next_timeout, &retry_find_peer_stop, &retry_context);
337 }
338
339 /**
340  * Signature of the main function of a task.
341  *
342  * @param cls closure
343  * @param tc context information (why was this task triggered now)
344  */
345 void
346 test_get_stop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
347 {
348   struct PeerContext *peer = cls;
349
350   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Called test_get_stop!\n");
351   if ( (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT) != 0)
352     {
353       GNUNET_break (0);
354       GNUNET_SCHEDULER_cancel (die_task);
355       GNUNET_SCHEDULER_add_now (&end_badly, NULL);
356       return;
357     }
358   GNUNET_assert (peer->dht_handle != NULL);
359   GNUNET_DHT_get_stop (peer->get_handle);
360   peer->get_handle = NULL;
361   GNUNET_SCHEDULER_add_now(&test_find_peer,
362                            &p1);
363 }
364
365 void
366 test_get_iterator (void *cls,
367                    struct GNUNET_TIME_Absolute exp,
368                    const GNUNET_HashCode * key,
369                    const struct GNUNET_PeerIdentity * const *get_path,
370                    const struct GNUNET_PeerIdentity * const *put_path,
371                    enum GNUNET_BLOCK_Type type, 
372                    size_t size, const void *data)
373 {
374   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
375               "test_get_iterator called (we got a result), stopping get request!\n");
376
377   GNUNET_SCHEDULER_add_continuation (&test_get_stop, &p1,
378                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
379 }
380
381 /**
382  * Signature of the main function of a task.
383  *
384  * @param cls closure
385  * @param tc context information (why was this task triggered now)
386  */
387 void
388 test_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
389 {
390   struct PeerContext *peer = cls;
391   GNUNET_HashCode hash;
392   memset (&hash, 42, sizeof (GNUNET_HashCode));
393
394   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Called test_get!\n");
395
396   GNUNET_assert (peer->dht_handle != NULL);
397   retry_context.real_timeout = GNUNET_TIME_relative_to_absolute(TOTAL_TIMEOUT);
398   retry_context.next_timeout = BASE_TIMEOUT;
399
400   peer->get_handle =
401     GNUNET_DHT_get_start (peer->dht_handle, 
402                           TOTAL_TIMEOUT,
403                           GNUNET_BLOCK_TYPE_TEST,
404                           &hash,
405                           DEFAULT_GET_REPLICATION,
406                           GNUNET_DHT_RO_NONE,
407                           NULL, 0,
408                           NULL, 0,
409                           &test_get_iterator, NULL);
410
411   if (peer->get_handle == NULL)
412     {
413       GNUNET_break (0);
414       GNUNET_SCHEDULER_cancel (die_task);
415       GNUNET_SCHEDULER_add_now (&end_badly, &p1);
416       return;
417     }
418
419   retry_context.peer_ctx = peer;
420 }
421
422 /**
423  * Signature of the main function of a task.
424  *
425  * @param cls closure
426  * @param tc context information (why was this task triggered now)
427  */
428 void
429 test_put (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
430 {
431   struct PeerContext *peer = cls;
432   GNUNET_HashCode hash;
433   char *data;
434   size_t data_size = 42;
435   memset (&hash, 42, sizeof (GNUNET_HashCode));
436   data = GNUNET_malloc (data_size);
437   memset (data, 43, data_size);
438   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Called test_put!\n");
439   peer->dht_handle = GNUNET_DHT_connect (peer->cfg, 100);
440
441   GNUNET_assert (peer->dht_handle != NULL);
442
443   GNUNET_DHT_put (peer->dht_handle, &hash, 
444                   DEFAULT_PUT_REPLICATION,
445                   GNUNET_DHT_RO_NONE,
446                   GNUNET_BLOCK_TYPE_TEST,
447                   data_size, data,
448                   GNUNET_TIME_relative_to_absolute (TOTAL_TIMEOUT),
449                   TOTAL_TIMEOUT,
450                   &test_get, &p1);
451   GNUNET_free(data);
452 }
453
454 static void
455 setup_peer (struct PeerContext *p, const char *cfgname)
456 {
457   p->cfg = GNUNET_CONFIGURATION_create ();
458 #if START_ARM
459   p->arm_proc = GNUNET_OS_start_process (NULL, NULL, "gnunet-service-arm",
460                                         "gnunet-service-arm",
461 #if VERBOSE_ARM
462                                         "-L", "DEBUG",
463 #endif
464                                         "-c", cfgname, NULL);
465 #endif
466   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
467
468 }
469
470 static void
471 run (void *cls,
472      char *const *args,
473      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
474 {
475   GNUNET_assert (ok == 1);
476   OKPP;
477
478   die_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
479                                            (GNUNET_TIME_UNIT_MINUTES, 1),
480                                            &end_badly, NULL);
481
482   setup_peer (&p1, "test_dht_api_peer1.conf");
483
484   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
485                                 (GNUNET_TIME_UNIT_SECONDS, 1), &test_put,
486                                 &p1);
487 }
488
489 static int
490 check ()
491 {
492
493   char *const argv[] = { "test-dht-api",
494     "-c",
495     "test_dht_api_data.conf",
496 #if VERBOSE
497     "-L", "DEBUG",
498 #endif
499     NULL
500   };
501
502   struct GNUNET_GETOPT_CommandLineOption options[] = {
503     GNUNET_GETOPT_OPTION_END
504   };
505
506   ok = 1;
507   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
508                       argv, "test-dht-api", "nohelp", options, &run, &ok);
509   stop_arm (&p1);
510   return ok;
511 }
512
513
514 int
515 main (int argc, char *argv[])
516 {
517   int ret;
518
519   GNUNET_log_setup ("test-dht-api",
520 #if VERBOSE
521                     "DEBUG",
522 #else
523                     "WARNING",
524 #endif
525                     NULL);
526   ret = check ();
527
528   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-dht-peer-1");
529
530   return ret;
531 }
532
533 /* end of test_dht_api.c */