ee8ce46c6868c237a5f956fe1dce13da35d25d9b
[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_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 total number of daemons to stop
429  * @param daemons array with the daemons (values will be clobbered)
430  */
431 void
432 GNUNET_FS_TEST_daemons_stop (struct GNUNET_SCHEDULER_Handle *sched,
433                              unsigned int total,
434                              struct GNUNET_FS_TestDaemon **daemons)
435 {
436   unsigned int i;
437
438   GNUNET_assert (total > 0);
439   GNUNET_TESTING_daemons_stop (daemons[0]->group);
440   for (i=0;i<total;i++)
441     {
442       GNUNET_FS_stop (daemons[i]->fs);
443       GNUNET_CONFIGURATION_destroy (daemons[i]->cfg);
444       GNUNET_free (daemons[i]);
445       daemons[i] = NULL;
446     }  
447 }
448
449
450 static void
451 publish_timeout (void *cls,
452                  const struct GNUNET_SCHEDULER_TaskContext *tc)
453 {
454   struct GNUNET_FS_TestDaemon *daemon = cls;
455   GNUNET_FS_TEST_UriContinuation cont;
456   
457   cont = daemon->publish_cont;
458   daemon->publish_timeout_task = GNUNET_SCHEDULER_NO_TASK;
459   daemon->publish_cont = NULL;
460   GNUNET_FS_publish_stop (daemon->publish_context);
461   daemon->publish_context = NULL;
462   cont (daemon->publish_cont_cls,
463         NULL);
464 }
465
466
467 static size_t
468 file_generator (void *cls, 
469                 uint64_t offset,
470                 size_t max, 
471                 void *buf,
472                 char **emsg)
473 {
474   struct GNUNET_FS_TestDaemon *daemon = cls;
475   uint64_t pos;
476   uint8_t *cbuf = buf;
477
478   for (pos=0;pos<max;pos++)
479     cbuf[pos] = (uint8_t) ((offset * daemon->publish_seed) % (255 - (offset / 1024 / 32)));  
480   return max;
481 }
482
483
484
485 /**
486  * Publish a file at the given daemon.
487  *
488  * @param sched scheduler to use
489  * @param daemon where to publish
490  * @param timeout if this operation cannot be completed within the
491  *                given period, call the continuation with an error code
492  * @param anonymity option for publication
493  * @param do_index GNUNET_YES for index, GNUNET_NO for insertion,
494  *                GNUNET_SYSERR for simulation
495  * @param size size of the file to publish
496  * @param seed seed to use for file generation
497  * @param verbose how verbose to be in reporting
498  * @param cont function to call when done
499  * @param cont_cls closure for cont
500  */
501 void
502 GNUNET_FS_TEST_publish (struct GNUNET_SCHEDULER_Handle *sched,
503                         struct GNUNET_FS_TestDaemon *daemon,
504                         struct GNUNET_TIME_Relative timeout,
505                         uint32_t anonymity,
506                         int do_index,
507                         uint64_t size,
508                         uint32_t seed,
509                         unsigned int verbose,
510                         GNUNET_FS_TEST_UriContinuation cont,
511                         void *cont_cls)
512 {
513   GNUNET_assert (daemon->publish_cont == NULL);
514   struct GNUNET_FS_FileInformation *fi;
515
516   daemon->publish_cont = cont;
517   daemon->publish_cont_cls = cont_cls;
518   daemon->publish_seed = seed;
519   daemon->verbose = verbose;
520   daemon->publish_sched = sched;
521   fi = GNUNET_FS_file_information_create_from_reader (daemon,
522                                                       size,
523                                                       &file_generator,
524                                                       daemon,
525                                                       NULL,
526                                                       NULL,
527                                                       do_index,
528                                                       anonymity,
529                                                       42 /* priority */,
530                                                       GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS));
531   daemon->publish_context = GNUNET_FS_publish_start (daemon->fs,
532                                                      fi,
533                                                      NULL, NULL, NULL,
534                                                      GNUNET_FS_PUBLISH_OPTION_NONE);
535   daemon->publish_timeout_task = GNUNET_SCHEDULER_add_delayed (sched,
536                                                                timeout,
537                                                                &publish_timeout,
538                                                                daemon);
539 }
540
541
542 static void
543 download_timeout (void *cls,
544                   const struct GNUNET_SCHEDULER_TaskContext *tc)
545 {
546   struct GNUNET_FS_TestDaemon *daemon = cls;
547
548   daemon->download_timeout_task = GNUNET_SCHEDULER_NO_TASK;
549   GNUNET_SCHEDULER_add_continuation (daemon->download_sched,
550                                      daemon->download_cont,
551                                      daemon->download_cont_cls,
552                                      GNUNET_SCHEDULER_REASON_TIMEOUT);
553   daemon->download_cont = NULL;
554   daemon->download_sched = NULL;
555 }
556
557
558 /**
559  * Perform test download.
560  *
561  * @param sched scheduler to use
562  * @param daemon which peer to download from
563  * @param timeout if this operation cannot be completed within the
564  *                given period, call the continuation with an error code
565  * @param anonymity option for download
566  * @param seed used for file validation
567  * @param uri URI of file to download (CHK/LOC only)
568  * @param verbose how verbose to be in reporting
569  * @param cont function to call when done
570  * @param cont_cls closure for cont
571  */
572 void
573 GNUNET_FS_TEST_download (struct GNUNET_SCHEDULER_Handle *sched,
574                          struct GNUNET_FS_TestDaemon *daemon,
575                          struct GNUNET_TIME_Relative timeout,
576                          uint32_t anonymity,
577                          uint32_t seed,
578                          const struct GNUNET_FS_Uri *uri,
579                          unsigned int verbose,
580                          GNUNET_SCHEDULER_Task cont,
581                          void *cont_cls)
582 {
583   uint64_t size;
584  
585   GNUNET_assert (daemon->download_cont == NULL);
586   size = GNUNET_FS_uri_chk_get_file_size (uri);
587   daemon->verbose = verbose;
588   daemon->download_sched = sched;
589   daemon->download_cont = cont;
590   daemon->download_cont_cls = cont_cls;
591   daemon->download_seed = seed;  
592   daemon->download_context = GNUNET_FS_download_start (daemon->fs,
593                                                        uri,
594                                                        NULL,
595                                                        NULL,
596                                                        0,
597                                                        size,
598                                                        anonymity,
599                                                        GNUNET_FS_DOWNLOAD_OPTION_NONE,
600                                                        NULL,
601                                                        NULL);
602   daemon->download_timeout_task = GNUNET_SCHEDULER_add_delayed (sched,
603                                                                 timeout,
604                                                                 &download_timeout,
605                                                                 daemon);
606 }
607
608 /* end of test_fs_lib.c */