1e3fa7f20b7a47305227d15c3f5fc3d0cb20c5bd
[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/test_fs_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_COMPLETED:
195       GNUNET_SCHEDULER_cancel (daemon->download_sched,
196                                daemon->download_timeout_task);
197       daemon->download_timeout_task = GNUNET_SCHEDULER_NO_TASK;
198       GNUNET_SCHEDULER_add_continuation (daemon->download_sched,
199                                          &report_success,
200                                          daemon,
201                                          GNUNET_SCHEDULER_REASON_PREREQ_DONE);
202       break;
203       /* FIXME: monitor data correctness during download progress */
204       /* FIXME: do performance reports given sufficient verbosity */
205       /* FIXME: advance timeout task to "immediate" on error */
206     default:
207       break;
208     }
209   return NULL;
210 }
211
212
213
214 struct StartContext
215 {
216   struct GNUNET_SCHEDULER_Handle *sched;
217   struct GNUNET_TIME_Relative timeout;
218   unsigned int total;
219   unsigned int have;
220   struct GNUNET_FS_TestDaemon **daemons;
221   GNUNET_SCHEDULER_Task cont;
222   void *cont_cls;
223   struct GNUNET_TESTING_PeerGroup *group;
224   struct GNUNET_CONFIGURATION_Handle *cfg;
225   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
226 };
227
228
229 static void 
230 notify_running (void *cls,
231                 const struct GNUNET_PeerIdentity *id,
232                 const struct GNUNET_CONFIGURATION_Handle *cfg,
233                 struct GNUNET_TESTING_Daemon *d,
234                 const char *emsg)
235 {
236   struct StartContext *sctx = cls;
237   unsigned int i;
238
239   if (emsg != NULL)
240     {
241       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
242                   _("Failed to start daemon: %s\n"),
243                   emsg);
244       return;
245     }
246   GNUNET_assert (sctx->have < sctx->total);
247   sctx->daemons[sctx->have]->cfg = GNUNET_CONFIGURATION_dup (cfg);
248   sctx->daemons[sctx->have]->group = sctx->group;
249   sctx->daemons[sctx->have]->daemon = d;
250   sctx->daemons[sctx->have]->id = *id;
251   sctx->have++;
252   if (sctx->have == sctx->total)
253     {
254       GNUNET_SCHEDULER_add_continuation (sctx->sched,
255                                          sctx->cont,
256                                          sctx->cont_cls,
257                                          GNUNET_SCHEDULER_REASON_PREREQ_DONE);
258       GNUNET_CONFIGURATION_destroy (sctx->cfg);
259       GNUNET_SCHEDULER_cancel (sctx->sched,
260                                sctx->timeout_task);
261       for (i=0;i<sctx->total;i++)
262         sctx->daemons[i]->fs = GNUNET_FS_start (sctx->sched,
263                                                 sctx->daemons[i]->cfg,
264                                                 "<tester>",
265                                                 &progress_cb,
266                                                 sctx->daemons[i],
267                                                 GNUNET_FS_FLAGS_NONE,
268                                                 GNUNET_FS_OPTIONS_END);
269       GNUNET_free (sctx);
270     }
271 }
272
273
274 static void
275 start_timeout (void *cls,
276                const struct GNUNET_SCHEDULER_TaskContext *tc)
277 {
278   struct StartContext *sctx = cls;
279   unsigned int i;
280
281   GNUNET_TESTING_daemons_stop (sctx->group);
282   for (i=0;i<sctx->total;i++)
283     {
284       if (i < sctx->have)
285         GNUNET_CONFIGURATION_destroy (sctx->daemons[i]->cfg);
286       GNUNET_free (sctx->daemons[i]);
287     }
288   GNUNET_CONFIGURATION_destroy (sctx->cfg);
289   GNUNET_SCHEDULER_add_continuation (sctx->sched,
290                                      sctx->cont,
291                                      sctx->cont_cls,
292                                      GNUNET_SCHEDULER_REASON_TIMEOUT);
293   GNUNET_free (sctx);
294 }
295
296
297 /**
298  * Start daemons for testing.
299  *
300  * @param sched scheduler to use
301  * @param timeout if this operation cannot be completed within the
302  *                given period, call the continuation with an error code
303  * @param total number of daemons to start
304  * @param daemons array of 'total' entries to be initialized
305  *                (array must already be allocated, will be filled)
306  * @param cont function to call when done
307  * @param cont_cls closure for cont
308  */
309 void
310 GNUNET_FS_TEST_daemons_start (struct GNUNET_SCHEDULER_Handle *sched,
311                               struct GNUNET_TIME_Relative timeout,
312                               unsigned int total,
313                               struct GNUNET_FS_TestDaemon **daemons,
314                               GNUNET_SCHEDULER_Task cont,
315                               void *cont_cls)
316 {
317   struct StartContext *sctx;
318   unsigned int i;
319
320   GNUNET_assert (total > 0);
321   sctx = GNUNET_malloc (sizeof (struct StartContext));
322   sctx->sched = sched;
323   sctx->daemons = daemons;
324   sctx->total = total;
325   sctx->cont = cont;
326   sctx->cont_cls = cont_cls;
327   sctx->cfg = GNUNET_CONFIGURATION_create ();
328   if (GNUNET_OK !=
329       GNUNET_CONFIGURATION_load (sctx->cfg,
330                                  "fs_test_lib_data.conf"))
331     {
332       GNUNET_break (0);
333       GNUNET_CONFIGURATION_destroy (sctx->cfg);
334       GNUNET_free (sctx);
335       GNUNET_SCHEDULER_add_continuation (sched,
336                                          cont,
337                                          cont_cls,
338                                          GNUNET_SCHEDULER_REASON_TIMEOUT);
339       return;
340     }
341   for (i=0;i<total;i++)
342     daemons[i] = GNUNET_malloc (sizeof (struct GNUNET_FS_TestDaemon));
343   sctx->group = GNUNET_TESTING_daemons_start (sched,
344                                               sctx->cfg,
345                                               total,
346                                               &notify_running,
347                                               sctx,
348                                               NULL, NULL,
349                                               NULL);
350   sctx->timeout_task = GNUNET_SCHEDULER_add_delayed (sched,
351                                                      timeout,
352                                                      &start_timeout,
353                                                      sctx);
354 }
355
356
357 struct ConnectContext
358 {
359   struct GNUNET_SCHEDULER_Handle *sched;
360   GNUNET_SCHEDULER_Task cont;
361   void *cont_cls;
362 };
363
364
365 static void
366 notify_connection (void *cls,
367                    const struct GNUNET_PeerIdentity *first,
368                    const struct GNUNET_PeerIdentity *second,
369                    const struct GNUNET_CONFIGURATION_Handle *first_cfg,
370                    const struct GNUNET_CONFIGURATION_Handle *second_cfg,
371                    struct GNUNET_TESTING_Daemon *first_daemon,
372                    struct GNUNET_TESTING_Daemon *second_daemon,
373                    const char *emsg)
374 {
375   struct ConnectContext *cc = cls;
376   
377   if (emsg != NULL)
378     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
379                 _("Failed to connect peers: %s\n"),
380                 emsg);
381   GNUNET_SCHEDULER_add_continuation (cc->sched,
382                                      cc->cont,
383                                      cc->cont_cls,
384                                      (emsg != NULL) 
385                                      ? GNUNET_SCHEDULER_REASON_TIMEOUT 
386                                      : GNUNET_SCHEDULER_REASON_PREREQ_DONE);
387   GNUNET_free (cc);
388 }
389
390
391 /**
392  * Connect two daemons for testing.
393  *
394  * @param sched scheduler to use
395  * @param daemon1 first daemon to connect
396  * @param daemon2 second first daemon to connect
397  * @param timeout if this operation cannot be completed within the
398  *                given period, call the continuation with an error code
399  * @param cont function to call when done
400  * @param cont_cls closure for cont
401  */
402 void
403 GNUNET_FS_TEST_daemons_connect (struct GNUNET_SCHEDULER_Handle *sched,
404                                 struct GNUNET_FS_TestDaemon *daemon1,
405                                 struct GNUNET_FS_TestDaemon *daemon2,
406                                 struct GNUNET_TIME_Relative timeout,
407                                 GNUNET_SCHEDULER_Task cont,
408                                 void *cont_cls)
409 {
410   struct ConnectContext *ncc;
411
412   ncc = GNUNET_malloc (sizeof (struct ConnectContext));
413   ncc->sched = sched;
414   ncc->cont = cont;
415   ncc->cont_cls = cont_cls;
416   GNUNET_TESTING_daemons_connect (daemon1->daemon,
417                                   daemon2->daemon,
418                                   timeout,
419                                   &notify_connection,
420                                   ncc);
421 }
422
423
424 /**
425  * Stop daemons used for testing.
426  *
427  * @param sched scheduler to use
428  * @param timeout if this operation cannot be completed within the
429  *                given period, call the continuation with an error code
430  * @param total number of daemons to stop
431  * @param daemons array with the daemons (values will be clobbered)
432  * @param cont function to call when done
433  * @param cont_cls closure for cont
434  */
435 void
436 GNUNET_FS_TEST_daemons_stop (struct GNUNET_SCHEDULER_Handle *sched,
437                              unsigned int total,
438                              struct GNUNET_FS_TestDaemon **daemons)
439 {
440   unsigned int i;
441
442   GNUNET_assert (total > 0);
443   GNUNET_TESTING_daemons_stop (daemons[0]->group);
444   for (i=0;i<total;i++)
445     {
446       GNUNET_FS_stop (daemons[i]->fs);
447       GNUNET_CONFIGURATION_destroy (daemons[i]->cfg);
448       GNUNET_free (daemons[i]);
449       daemons[i] = NULL;
450     }  
451 }
452
453
454 static void
455 publish_timeout (void *cls,
456                  const struct GNUNET_SCHEDULER_TaskContext *tc)
457 {
458   struct GNUNET_FS_TestDaemon *daemon = cls;
459   GNUNET_FS_TEST_UriContinuation cont;
460   
461   cont = daemon->publish_cont;
462   daemon->publish_timeout_task = GNUNET_SCHEDULER_NO_TASK;
463   daemon->publish_cont = NULL;
464   GNUNET_FS_publish_stop (daemon->publish_context);
465   daemon->publish_context = NULL;
466   cont (daemon->publish_cont_cls,
467         NULL);
468 }
469
470
471 static size_t
472 file_generator (void *cls, 
473                 uint64_t offset,
474                 size_t max, 
475                 void *buf,
476                 char **emsg)
477 {
478   struct GNUNET_FS_TestDaemon *daemon = cls;
479   uint64_t pos;
480   uint8_t *cbuf = buf;
481
482   for (pos=0;pos<max;pos++)
483     cbuf[pos] = (uint8_t) ((offset * daemon->publish_seed) % (255 - (offset / 1024 / 32)));  
484   return max;
485 }
486
487
488
489 /**
490  * Publish a file at the given daemon.
491  *
492  * @param sched scheduler to use
493  * @param daemon where to publish
494  * @param timeout if this operation cannot be completed within the
495  *                given period, call the continuation with an error code
496  * @param anonymity option for publication
497  * @param do_index GNUNET_YES for index, GNUNET_NO for insertion,
498  *                GNUNET_SYSERR for simulation
499  * @param size size of the file to publish
500  * @param seed seed to use for file generation
501  * @param verbose how verbose to be in reporting
502  * @param cont function to call when done
503  * @param cont_cls closure for cont
504  */
505 void
506 GNUNET_FS_TEST_publish (struct GNUNET_SCHEDULER_Handle *sched,
507                         struct GNUNET_FS_TestDaemon *daemon,
508                         struct GNUNET_TIME_Relative timeout,
509                         uint32_t anonymity,
510                         int do_index,
511                         uint64_t size,
512                         uint32_t seed,
513                         unsigned int verbose,
514                         GNUNET_FS_TEST_UriContinuation cont,
515                         void *cont_cls)
516 {
517   GNUNET_assert (daemon->publish_cont == NULL);
518   struct GNUNET_FS_FileInformation *fi;
519
520   daemon->publish_cont = cont;
521   daemon->publish_cont_cls = cont_cls;
522   daemon->publish_seed = seed;
523   daemon->verbose = verbose;
524   daemon->publish_sched = sched;
525   fi = GNUNET_FS_file_information_create_from_reader (daemon,
526                                                       size,
527                                                       &file_generator,
528                                                       daemon,
529                                                       NULL,
530                                                       NULL,
531                                                       do_index,
532                                                       anonymity,
533                                                       42 /* priority */,
534                                                       GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS));
535   daemon->publish_context = GNUNET_FS_publish_start (daemon->fs,
536                                                      fi,
537                                                      NULL, NULL, NULL,
538                                                      GNUNET_FS_PUBLISH_OPTION_NONE);
539   daemon->publish_timeout_task = GNUNET_SCHEDULER_add_delayed (sched,
540                                                                timeout,
541                                                                &publish_timeout,
542                                                                daemon);
543 }
544
545
546 static void
547 download_timeout (void *cls,
548                   const struct GNUNET_SCHEDULER_TaskContext *tc)
549 {
550   struct GNUNET_FS_TestDaemon *daemon = cls;
551
552   daemon->download_timeout_task = GNUNET_SCHEDULER_NO_TASK;
553   GNUNET_SCHEDULER_add_continuation (daemon->download_sched,
554                                      daemon->download_cont,
555                                      daemon->download_cont_cls,
556                                      GNUNET_SCHEDULER_REASON_TIMEOUT);
557   daemon->download_cont = NULL;
558   daemon->download_sched = NULL;
559 }
560
561
562 /**
563  * Perform test download.
564  *
565  * @param sched scheduler to use
566  * @param daemon which peer to download from
567  * @param timeout if this operation cannot be completed within the
568  *                given period, call the continuation with an error code
569  * @param anonymity option for download
570  * @param seed used for file validation
571  * @param verbose how verbose to be in reporting
572  * @param cont function to call when done
573  * @param cont_cls closure for cont
574  */
575 void
576 GNUNET_FS_TEST_download (struct GNUNET_SCHEDULER_Handle *sched,
577                          struct GNUNET_FS_TestDaemon *daemon,
578                          struct GNUNET_TIME_Relative timeout,
579                          uint32_t anonymity,
580                          uint32_t seed,
581                          const struct GNUNET_FS_Uri *uri,
582                          unsigned int verbose,
583                          GNUNET_SCHEDULER_Task cont,
584                          void *cont_cls)
585 {
586   uint64_t size;
587  
588   GNUNET_assert (daemon->download_cont == NULL);
589   size = GNUNET_FS_uri_chk_get_file_size (uri);
590   daemon->verbose = verbose;
591   daemon->download_sched = sched;
592   daemon->download_cont = cont;
593   daemon->download_cont_cls = cont_cls;
594   daemon->download_seed = seed;  
595   daemon->download_context = GNUNET_FS_download_start (daemon->fs,
596                                                        uri,
597                                                        NULL,
598                                                        NULL,
599                                                        0,
600                                                        size,
601                                                        anonymity,
602                                                        GNUNET_FS_DOWNLOAD_OPTION_NONE,
603                                                        NULL,
604                                                        NULL);
605   daemon->download_timeout_task = GNUNET_SCHEDULER_add_delayed (sched,
606                                                                 timeout,
607                                                                 &download_timeout,
608                                                                 daemon);
609 }
610
611 /* end of test_fs_lib.c */