doxygen fixes
[oweals/gnunet.git] / src / fs / fs_test_lib.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010 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 /**
22  * @file fs/fs_test_lib.c
23  * @brief library routines for testing FS publishing and downloading
24  *        with multiple peers; this code is limited to flat files
25  *        and no keywords (those functions can be tested with
26  *        single-peer setups; this is for testing routing).
27  * @author Christian Grothoff
28  */
29 #include "platform.h"
30 #include "fs_test_lib.h"
31 #include "gnunet_testing_lib.h"
32
33
34 /**
35  * Handle for a daemon started for testing FS.
36  */
37 struct GNUNET_FS_TestDaemon
38 {
39
40   /**
41    * Handle to the file sharing context using this daemon.
42    */
43   struct GNUNET_FS_Handle *fs;
44
45   /**
46    * Handle to the daemon via testing.
47    */
48   struct GNUNET_TESTING_Daemon *daemon;
49
50   /**
51    * Note that 'group' will be the same value for all of the
52    * daemons started jointly.
53    */
54   struct GNUNET_TESTING_PeerGroup *group;
55
56   /**
57    * Configuration for accessing this peer.
58    */
59   struct GNUNET_CONFIGURATION_Handle *cfg;
60
61   /**
62    * ID of this peer.
63    */
64   struct GNUNET_PeerIdentity id;
65
66   /**
67    * Scheduler to use (for publish_cont).
68    */
69   struct GNUNET_SCHEDULER_Handle *publish_sched;
70
71   /**
72    * Function to call when upload is done.
73    */
74   GNUNET_FS_TEST_UriContinuation publish_cont;
75   
76   /**
77    * Closure for publish_cont.
78    */
79   void *publish_cont_cls;
80
81   /**
82    * Task to abort publishing (timeout).
83    */
84   GNUNET_SCHEDULER_TaskIdentifier publish_timeout_task;
85
86   /**
87    * Seed for file generation.
88    */
89   uint32_t publish_seed;
90
91   /**
92    * Context for current publishing operation.
93    */
94   struct GNUNET_FS_PublishContext *publish_context;
95
96   /**
97    * Result URI.
98    */
99   struct GNUNET_FS_Uri *publish_uri;
100
101   /**
102    * Scheduler to use (for download_cont).
103    */
104   struct GNUNET_SCHEDULER_Handle *download_sched;
105
106   /**
107    * Function to call when download is done.
108    */
109   GNUNET_SCHEDULER_Task download_cont;
110
111   /**
112    * Closure for download_cont.
113    */
114   void *download_cont_cls;
115
116   /**
117    * Seed for download verification.
118    */
119   uint32_t download_seed;
120
121   /**
122    * Task to abort downloading (timeout).
123    */
124   GNUNET_SCHEDULER_TaskIdentifier download_timeout_task;
125
126   /**
127    * Context for current download operation.
128    */  
129   struct GNUNET_FS_DownloadContext *download_context;
130
131   /**
132    * Verbosity level of the current operation.
133    */
134   int verbose;
135
136                 
137 };
138
139
140 static void
141 report_uri (void *cls,
142             const struct GNUNET_SCHEDULER_TaskContext *tc)
143 {
144   struct GNUNET_FS_TestDaemon *daemon = cls;
145   GNUNET_FS_TEST_UriContinuation cont;
146   struct GNUNET_FS_Uri *uri;
147
148   GNUNET_FS_publish_stop (daemon->publish_context);
149   daemon->publish_context = NULL;
150   daemon->publish_sched = NULL;
151   cont = daemon->publish_cont;
152   daemon->publish_cont = NULL;
153   uri = daemon->publish_uri;
154   cont (daemon->publish_cont_cls,
155         uri);
156   GNUNET_FS_uri_destroy (uri);
157 }            
158
159
160 static void
161 report_success (void *cls,
162                 const struct GNUNET_SCHEDULER_TaskContext *tc)
163 {
164   struct GNUNET_FS_TestDaemon *daemon = cls;
165
166   GNUNET_FS_download_stop (daemon->download_context, GNUNET_YES);
167   daemon->download_context = NULL;
168   GNUNET_SCHEDULER_add_continuation (daemon->download_sched,
169                                      daemon->download_cont,
170                                      daemon->download_cont_cls,
171                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);      
172   daemon->download_cont = NULL;
173   daemon->download_sched = NULL;
174 }
175
176 static void*
177 progress_cb (void *cls,
178              const struct GNUNET_FS_ProgressInfo *info)
179 {
180   struct GNUNET_FS_TestDaemon *daemon = cls;
181
182   switch (info->status)
183     {
184     case GNUNET_FS_STATUS_PUBLISH_COMPLETED:      
185       GNUNET_SCHEDULER_cancel (daemon->publish_sched,
186                                daemon->publish_timeout_task);
187       daemon->publish_timeout_task = GNUNET_SCHEDULER_NO_TASK;
188       daemon->publish_uri = GNUNET_FS_uri_dup (info->value.publish.specifics.completed.chk_uri);
189       GNUNET_SCHEDULER_add_continuation (daemon->publish_sched,
190                                          &report_uri,
191                                          daemon,
192                                          GNUNET_SCHEDULER_REASON_PREREQ_DONE);
193       break;
194     case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
195       if (daemon->verbose)
196         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
197                     "Download at %llu/%llu bytes\n",
198                     (unsigned long long) info->value.download.completed,
199                     (unsigned long long) info->value.download.size);
200       break;
201     case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
202       GNUNET_SCHEDULER_cancel (daemon->download_sched,
203                                daemon->download_timeout_task);
204       daemon->download_timeout_task = GNUNET_SCHEDULER_NO_TASK;
205       GNUNET_SCHEDULER_add_continuation (daemon->download_sched,
206                                          &report_success,
207                                          daemon,
208                                          GNUNET_SCHEDULER_REASON_PREREQ_DONE);
209       break;
210       /* FIXME: monitor data correctness during download progress */
211       /* FIXME: do performance reports given sufficient verbosity */
212       /* FIXME: advance timeout task to "immediate" on error */
213     default:
214       break;
215     }
216   return NULL;
217 }
218
219
220
221 struct StartContext
222 {
223   struct GNUNET_SCHEDULER_Handle *sched;
224   struct GNUNET_TIME_Relative timeout;
225   unsigned int total;
226   unsigned int have;
227   struct GNUNET_FS_TestDaemon **daemons;
228   GNUNET_SCHEDULER_Task cont;
229   void *cont_cls;
230   struct GNUNET_TESTING_PeerGroup *group;
231   struct GNUNET_CONFIGURATION_Handle *cfg;
232   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
233 };
234
235
236 static void 
237 notify_running (void *cls,
238                 const struct GNUNET_PeerIdentity *id,
239                 const struct GNUNET_CONFIGURATION_Handle *cfg,
240                 struct GNUNET_TESTING_Daemon *d,
241                 const char *emsg)
242 {
243   struct StartContext *sctx = cls;
244   unsigned int i;
245
246   if (emsg != NULL)
247     {
248       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
249                   _("Failed to start daemon: %s\n"),
250                   emsg);
251       return;
252     }
253   GNUNET_assert (sctx->have < sctx->total);
254   sctx->daemons[sctx->have]->cfg = GNUNET_CONFIGURATION_dup (cfg);
255   sctx->daemons[sctx->have]->group = sctx->group;
256   sctx->daemons[sctx->have]->daemon = d;
257   sctx->daemons[sctx->have]->id = *id;
258   sctx->have++;
259   if (sctx->have == sctx->total)
260     {
261       GNUNET_SCHEDULER_add_continuation (sctx->sched,
262                                          sctx->cont,
263                                          sctx->cont_cls,
264                                          GNUNET_SCHEDULER_REASON_PREREQ_DONE);
265       GNUNET_CONFIGURATION_destroy (sctx->cfg);
266       GNUNET_SCHEDULER_cancel (sctx->sched,
267                                sctx->timeout_task);
268       for (i=0;i<sctx->total;i++)
269         sctx->daemons[i]->fs = GNUNET_FS_start (sctx->sched,
270                                                 sctx->daemons[i]->cfg,
271                                                 "<tester>",
272                                                 &progress_cb,
273                                                 sctx->daemons[i],
274                                                 GNUNET_FS_FLAGS_NONE,
275                                                 GNUNET_FS_OPTIONS_END);
276       GNUNET_free (sctx);
277     }
278 }
279
280
281 static void
282 start_timeout (void *cls,
283                const struct GNUNET_SCHEDULER_TaskContext *tc)
284 {
285   struct StartContext *sctx = cls;
286   unsigned int i;
287
288   GNUNET_TESTING_daemons_stop (sctx->group);
289   for (i=0;i<sctx->total;i++)
290     {
291       if (i < sctx->have)
292         GNUNET_CONFIGURATION_destroy (sctx->daemons[i]->cfg);
293       GNUNET_free (sctx->daemons[i]);
294     }
295   GNUNET_CONFIGURATION_destroy (sctx->cfg);
296   GNUNET_SCHEDULER_add_continuation (sctx->sched,
297                                      sctx->cont,
298                                      sctx->cont_cls,
299                                      GNUNET_SCHEDULER_REASON_TIMEOUT);
300   GNUNET_free (sctx);
301 }
302
303
304 /**
305  * Start daemons for testing.
306  *
307  * @param sched scheduler to use
308  * @param timeout if this operation cannot be completed within the
309  *                given period, call the continuation with an error code
310  * @param total number of daemons to start
311  * @param daemons array of 'total' entries to be initialized
312  *                (array must already be allocated, will be filled)
313  * @param cont function to call when done
314  * @param cont_cls closure for cont
315  */
316 void
317 GNUNET_FS_TEST_daemons_start (struct GNUNET_SCHEDULER_Handle *sched,
318                               struct GNUNET_TIME_Relative timeout,
319                               unsigned int total,
320                               struct GNUNET_FS_TestDaemon **daemons,
321                               GNUNET_SCHEDULER_Task cont,
322                               void *cont_cls)
323 {
324   struct StartContext *sctx;
325   unsigned int i;
326
327   GNUNET_assert (total > 0);
328   sctx = GNUNET_malloc (sizeof (struct StartContext));
329   sctx->sched = sched;
330   sctx->daemons = daemons;
331   sctx->total = total;
332   sctx->cont = cont;
333   sctx->cont_cls = cont_cls;
334   sctx->cfg = GNUNET_CONFIGURATION_create ();
335   if (GNUNET_OK !=
336       GNUNET_CONFIGURATION_load (sctx->cfg,
337                                  "fs_test_lib_data.conf"))
338     {
339       GNUNET_break (0);
340       GNUNET_CONFIGURATION_destroy (sctx->cfg);
341       GNUNET_free (sctx);
342       GNUNET_SCHEDULER_add_continuation (sched,
343                                          cont,
344                                          cont_cls,
345                                          GNUNET_SCHEDULER_REASON_TIMEOUT);
346       return;
347     }
348   for (i=0;i<total;i++)
349     daemons[i] = GNUNET_malloc (sizeof (struct GNUNET_FS_TestDaemon));
350   sctx->group = GNUNET_TESTING_daemons_start (sched,
351                                               sctx->cfg,
352                                               total,
353                                               &notify_running,
354                                               sctx,
355                                               NULL, NULL,
356                                               NULL);
357   sctx->timeout_task = GNUNET_SCHEDULER_add_delayed (sched,
358                                                      timeout,
359                                                      &start_timeout,
360                                                      sctx);
361 }
362
363
364 struct ConnectContext
365 {
366   struct GNUNET_SCHEDULER_Handle *sched;
367   GNUNET_SCHEDULER_Task cont;
368   void *cont_cls;
369 };
370
371
372 static void
373 notify_connection (void *cls,
374                    const struct GNUNET_PeerIdentity *first,
375                    const struct GNUNET_PeerIdentity *second,
376                    const struct GNUNET_CONFIGURATION_Handle *first_cfg,
377                    const struct GNUNET_CONFIGURATION_Handle *second_cfg,
378                    struct GNUNET_TESTING_Daemon *first_daemon,
379                    struct GNUNET_TESTING_Daemon *second_daemon,
380                    const char *emsg)
381 {
382   struct ConnectContext *cc = cls;
383   
384   if (emsg != NULL)
385     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
386                 _("Failed to connect peers: %s\n"),
387                 emsg);
388   GNUNET_SCHEDULER_add_continuation (cc->sched,
389                                      cc->cont,
390                                      cc->cont_cls,
391                                      (emsg != NULL) 
392                                      ? GNUNET_SCHEDULER_REASON_TIMEOUT 
393                                      : GNUNET_SCHEDULER_REASON_PREREQ_DONE);
394   GNUNET_free (cc);
395 }
396
397
398 /**
399  * Connect two daemons for testing.
400  *
401  * @param sched scheduler to use
402  * @param daemon1 first daemon to connect
403  * @param daemon2 second first daemon to connect
404  * @param timeout if this operation cannot be completed within the
405  *                given period, call the continuation with an error code
406  * @param cont function to call when done
407  * @param cont_cls closure for cont
408  */
409 void
410 GNUNET_FS_TEST_daemons_connect (struct GNUNET_SCHEDULER_Handle *sched,
411                                 struct GNUNET_FS_TestDaemon *daemon1,
412                                 struct GNUNET_FS_TestDaemon *daemon2,
413                                 struct GNUNET_TIME_Relative timeout,
414                                 GNUNET_SCHEDULER_Task cont,
415                                 void *cont_cls)
416 {
417   struct ConnectContext *ncc;
418
419   ncc = GNUNET_malloc (sizeof (struct ConnectContext));
420   ncc->sched = sched;
421   ncc->cont = cont;
422   ncc->cont_cls = cont_cls;
423   GNUNET_TESTING_daemons_connect (daemon1->daemon,
424                                   daemon2->daemon,
425                                   timeout,
426                                   &notify_connection,
427                                   ncc);
428 }
429
430
431 /**
432  * Stop daemons used for testing.
433  *
434  * @param sched scheduler to use
435  * @param total number of daemons to stop
436  * @param daemons array with the daemons (values will be clobbered)
437  */
438 void
439 GNUNET_FS_TEST_daemons_stop (struct GNUNET_SCHEDULER_Handle *sched,
440                              unsigned int total,
441                              struct GNUNET_FS_TestDaemon **daemons)
442 {
443   unsigned int i;
444
445   GNUNET_assert (total > 0);
446   GNUNET_TESTING_daemons_stop (daemons[0]->group);
447   for (i=0;i<total;i++)
448     {
449       GNUNET_FS_stop (daemons[i]->fs);
450       GNUNET_CONFIGURATION_destroy (daemons[i]->cfg);
451       GNUNET_free (daemons[i]);
452       daemons[i] = NULL;
453     }  
454 }
455
456
457 static void
458 publish_timeout (void *cls,
459                  const struct GNUNET_SCHEDULER_TaskContext *tc)
460 {
461   struct GNUNET_FS_TestDaemon *daemon = cls;
462   GNUNET_FS_TEST_UriContinuation cont;
463   
464   cont = daemon->publish_cont;
465   daemon->publish_timeout_task = GNUNET_SCHEDULER_NO_TASK;
466   daemon->publish_cont = NULL;
467   GNUNET_FS_publish_stop (daemon->publish_context);
468   daemon->publish_context = NULL;
469   cont (daemon->publish_cont_cls,
470         NULL);
471 }
472
473
474 static size_t
475 file_generator (void *cls, 
476                 uint64_t offset,
477                 size_t max, 
478                 void *buf,
479                 char **emsg)
480 {
481   struct GNUNET_FS_TestDaemon *daemon = cls;
482   uint64_t pos;
483   uint8_t *cbuf = buf;
484   int mod;
485
486   for (pos=0;pos<max;pos++)
487     {
488       mod = (255 - (offset / 1024 / 32));
489       if (mod == 0)
490         mod = 1;
491       cbuf[pos] = (uint8_t) ((offset * daemon->publish_seed) % mod);  
492     }
493   return max;
494 }
495
496
497
498 /**
499  * Publish a file at the given daemon.
500  *
501  * @param sched scheduler to use
502  * @param daemon where to publish
503  * @param timeout if this operation cannot be completed within the
504  *                given period, call the continuation with an error code
505  * @param anonymity option for publication
506  * @param do_index GNUNET_YES for index, GNUNET_NO for insertion,
507  *                GNUNET_SYSERR for simulation
508  * @param size size of the file to publish
509  * @param seed seed to use for file generation
510  * @param verbose how verbose to be in reporting
511  * @param cont function to call when done
512  * @param cont_cls closure for cont
513  */
514 void
515 GNUNET_FS_TEST_publish (struct GNUNET_SCHEDULER_Handle *sched,
516                         struct GNUNET_FS_TestDaemon *daemon,
517                         struct GNUNET_TIME_Relative timeout,
518                         uint32_t anonymity,
519                         int do_index,
520                         uint64_t size,
521                         uint32_t seed,
522                         unsigned int verbose,
523                         GNUNET_FS_TEST_UriContinuation cont,
524                         void *cont_cls)
525 {
526   GNUNET_assert (daemon->publish_cont == NULL);
527   struct GNUNET_FS_FileInformation *fi;
528
529   daemon->publish_cont = cont;
530   daemon->publish_cont_cls = cont_cls;
531   daemon->publish_seed = seed;
532   daemon->verbose = verbose;
533   daemon->publish_sched = sched;
534   fi = GNUNET_FS_file_information_create_from_reader (daemon,
535                                                       size,
536                                                       &file_generator,
537                                                       daemon,
538                                                       NULL,
539                                                       NULL,
540                                                       do_index,
541                                                       anonymity,
542                                                       42 /* priority */,
543                                                       GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS));
544   daemon->publish_context = GNUNET_FS_publish_start (daemon->fs,
545                                                      fi,
546                                                      NULL, NULL, NULL,
547                                                      GNUNET_FS_PUBLISH_OPTION_NONE);
548   daemon->publish_timeout_task = GNUNET_SCHEDULER_add_delayed (sched,
549                                                                timeout,
550                                                                &publish_timeout,
551                                                                daemon);
552 }
553
554
555 static void
556 download_timeout (void *cls,
557                   const struct GNUNET_SCHEDULER_TaskContext *tc)
558 {
559   struct GNUNET_FS_TestDaemon *daemon = cls;
560
561   daemon->download_timeout_task = GNUNET_SCHEDULER_NO_TASK;
562   GNUNET_FS_download_stop (daemon->download_context, GNUNET_YES);
563   daemon->download_context = NULL;
564   GNUNET_SCHEDULER_add_continuation (daemon->download_sched,
565                                      daemon->download_cont,
566                                      daemon->download_cont_cls,
567                                      GNUNET_SCHEDULER_REASON_TIMEOUT);
568   daemon->download_cont = NULL;
569   daemon->download_sched = NULL;
570 }
571
572
573 /**
574  * Perform test download.
575  *
576  * @param sched scheduler to use
577  * @param daemon which peer to download from
578  * @param timeout if this operation cannot be completed within the
579  *                given period, call the continuation with an error code
580  * @param anonymity option for download
581  * @param seed used for file validation
582  * @param uri URI of file to download (CHK/LOC only)
583  * @param verbose how verbose to be in reporting
584  * @param cont function to call when done
585  * @param cont_cls closure for cont
586  */
587 void
588 GNUNET_FS_TEST_download (struct GNUNET_SCHEDULER_Handle *sched,
589                          struct GNUNET_FS_TestDaemon *daemon,
590                          struct GNUNET_TIME_Relative timeout,
591                          uint32_t anonymity,
592                          uint32_t seed,
593                          const struct GNUNET_FS_Uri *uri,
594                          unsigned int verbose,
595                          GNUNET_SCHEDULER_Task cont,
596                          void *cont_cls)
597 {
598   uint64_t size;
599  
600   GNUNET_assert (daemon->download_cont == NULL);
601   size = GNUNET_FS_uri_chk_get_file_size (uri);
602   daemon->verbose = verbose;
603   daemon->download_sched = sched;
604   daemon->download_cont = cont;
605   daemon->download_cont_cls = cont_cls;
606   daemon->download_seed = seed;  
607   daemon->download_context = GNUNET_FS_download_start (daemon->fs,
608                                                        uri,
609                                                        NULL,
610                                                        NULL,
611                                                        0,
612                                                        size,
613                                                        anonymity,
614                                                        GNUNET_FS_DOWNLOAD_OPTION_NONE,
615                                                        NULL,
616                                                        NULL);
617   daemon->download_timeout_task = GNUNET_SCHEDULER_add_delayed (sched,
618                                                                 timeout,
619                                                                 &download_timeout,
620                                                                 daemon);
621 }
622
623 /* end of test_fs_lib.c */