-LRN: Also break on commas
[oweals/gnunet.git] / src / fs / test_fs_download_persistence.c
1 /*
2      This file is part of GNUnet.
3      (C) 2004, 2005, 2006, 2008, 2009, 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 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 /**
22  * @file fs/test_fs_download_persistence.c
23  * @brief simple testcase for persistence of simple download operation
24  * @author Christian Grothoff
25  */
26
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_arm_service.h"
30 #include "gnunet_fs_service.h"
31
32 #define VERBOSE GNUNET_EXTRA_LOGGING
33
34 #define START_ARM GNUNET_YES
35
36 /**
37  * File-size we use for testing.
38  */
39 #define FILESIZE (1024 * 1024 * 2)
40
41 /**
42  * How long until we give up on transmitting the message?
43  */
44 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
45
46 /**
47  * How long should our test-content live?
48  */
49 #define LIFETIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
50
51 struct PeerContext
52 {
53   struct GNUNET_CONFIGURATION_Handle *cfg;
54 #if START_ARM
55   struct GNUNET_OS_Process *arm_proc;
56 #endif
57 };
58
59 static struct PeerContext p1;
60
61 static struct GNUNET_TIME_Absolute start;
62
63 static const struct GNUNET_CONFIGURATION_Handle *cfg;
64
65 static struct GNUNET_FS_Handle *fs;
66
67 static struct GNUNET_FS_DownloadContext *download;
68
69 static struct GNUNET_FS_PublishContext *publish;
70
71 static GNUNET_SCHEDULER_TaskIdentifier timeout_kill;
72
73 static char *fn;
74
75 static int err;
76
77 static void
78 timeout_kill_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
79 {
80   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Timeout downloading file\n");
81   if (download != NULL)
82   {
83     GNUNET_FS_download_stop (download, GNUNET_YES);
84     download = NULL;
85   }
86   else if (publish != NULL)
87   {
88     GNUNET_FS_publish_stop (publish);
89     publish = NULL;
90   }
91   timeout_kill = GNUNET_SCHEDULER_NO_TASK;
92   err = 1;
93 }
94
95 static void
96 abort_publish_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
97 {
98   if (publish != NULL)
99   {
100     GNUNET_FS_publish_stop (publish);
101     publish = NULL;
102   }
103 }
104
105
106 static void
107 abort_download_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
108 {
109   uint64_t size;
110
111   if (download != NULL)
112   {
113     GNUNET_FS_download_stop (download, GNUNET_YES);
114     download = NULL;
115   }
116   GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_size (fn, &size, GNUNET_YES));
117   GNUNET_assert (size == FILESIZE);
118   GNUNET_DISK_directory_remove (fn);
119   GNUNET_free (fn);
120   fn = NULL;
121   GNUNET_SCHEDULER_cancel (timeout_kill);
122   timeout_kill = GNUNET_SCHEDULER_NO_TASK;
123 }
124
125
126 static void *
127 progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event);
128
129
130 static void
131 restart_fs_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
132 {
133   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Restarting FS.\n");
134   GNUNET_FS_stop (fs);
135   fs = GNUNET_FS_start (cfg, "test-fs-download-persistence", &progress_cb, NULL,
136                         GNUNET_FS_FLAGS_PERSISTENCE, GNUNET_FS_OPTIONS_END);
137 }
138
139
140 /**
141  * Consider scheduling the restart-task.
142  * Only runs the restart task once per event
143  * category.
144  *
145  * @param ev type of the event to consider
146  */
147 static void
148 consider_restart (int ev)
149 {
150   static int prev[32];
151   static int off;
152   int i;
153
154   for (i = 0; i < off; i++)
155     if (prev[i] == ev)
156       return;
157   prev[off++] = ev;
158   GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_URGENT,
159                                       &restart_fs_task, NULL);
160 }
161
162
163 static void *
164 progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
165 {
166   switch (event->status)
167   {
168   case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
169 #if VERBOSE
170     printf ("Publish is progressing (%llu/%llu at level %u off %llu)...\n",
171             (unsigned long long) event->value.publish.completed,
172             (unsigned long long) event->value.publish.size,
173             event->value.publish.specifics.progress.depth,
174             (unsigned long long) event->value.publish.specifics.
175             progress.offset);
176 #endif
177     break;
178   case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
179     printf ("Publishing complete, %llu kbps.\n",
180             (unsigned long long) (FILESIZE * 1000LL /
181                                   (1 +
182                                    GNUNET_TIME_absolute_get_duration
183                                    (start).rel_value) / 1024LL));
184     fn = GNUNET_DISK_mktemp ("gnunet-download-test-dst");
185     start = GNUNET_TIME_absolute_get ();
186     GNUNET_assert (download == NULL);
187     GNUNET_FS_download_start (fs,
188                               event->value.publish.specifics.completed.chk_uri,
189                               NULL, fn, NULL, 0, FILESIZE, 1,
190                               GNUNET_FS_DOWNLOAD_OPTION_NONE, "download", NULL);
191     break;
192   case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
193     consider_restart (event->status);
194     printf ("Download complete,  %llu kbps.\n",
195             (unsigned long long) (FILESIZE * 1000LL /
196                                   (1 +
197                                    GNUNET_TIME_absolute_get_duration
198                                    (start).rel_value) / 1024LL));
199     GNUNET_SCHEDULER_add_now (&abort_download_task, NULL);
200     break;
201   case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
202     consider_restart (event->status);
203     GNUNET_assert (download == event->value.download.dc);
204 #if VERBOSE
205     printf ("Download is progressing (%llu/%llu at level %u off %llu)...\n",
206             (unsigned long long) event->value.download.completed,
207             (unsigned long long) event->value.download.size,
208             event->value.download.specifics.progress.depth,
209             (unsigned long long) event->value.download.specifics.
210             progress.offset);
211 #endif
212     break;
213   case GNUNET_FS_STATUS_PUBLISH_ERROR:
214     fprintf (stderr, "Error publishing file: %s\n",
215              event->value.publish.specifics.error.message);
216     GNUNET_break (0);
217     GNUNET_SCHEDULER_add_continuation (&abort_publish_task, NULL,
218                                        GNUNET_SCHEDULER_REASON_PREREQ_DONE);
219     break;
220   case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
221     fprintf (stderr, "Error downloading file: %s\n",
222              event->value.download.specifics.error.message);
223     GNUNET_SCHEDULER_add_now (&abort_download_task, NULL);
224     break;
225   case GNUNET_FS_STATUS_PUBLISH_SUSPEND:
226     GNUNET_assert (event->value.publish.pc == publish);
227     publish = NULL;
228     break;
229   case GNUNET_FS_STATUS_PUBLISH_RESUME:
230     GNUNET_assert (NULL == publish);
231     publish = event->value.publish.pc;
232     break;
233   case GNUNET_FS_STATUS_DOWNLOAD_SUSPEND:
234     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download suspended.\n");
235     GNUNET_assert (event->value.download.dc == download);
236     download = NULL;
237     break;
238   case GNUNET_FS_STATUS_DOWNLOAD_RESUME:
239     GNUNET_assert (NULL == download);
240     download = event->value.download.dc;
241     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download resumed.\n");
242     break;
243   case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
244     consider_restart (event->status);
245     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download active.\n");
246     break;
247   case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
248     consider_restart (event->status);
249     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download inactive.\n");
250     break;
251   case GNUNET_FS_STATUS_PUBLISH_START:
252     GNUNET_assert (0 == strcmp ("publish-context", event->value.publish.cctx));
253     GNUNET_assert (NULL == event->value.publish.pctx);
254     GNUNET_assert (FILESIZE == event->value.publish.size);
255     GNUNET_assert (0 == event->value.publish.completed);
256     GNUNET_assert (1 == event->value.publish.anonymity);
257     break;
258   case GNUNET_FS_STATUS_PUBLISH_STOPPED:
259     GNUNET_assert (publish == event->value.publish.pc);
260     GNUNET_assert (FILESIZE == event->value.publish.size);
261     GNUNET_assert (1 == event->value.publish.anonymity);
262     GNUNET_FS_stop (fs);
263     fs = NULL;
264     break;
265   case GNUNET_FS_STATUS_DOWNLOAD_START:
266     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download started.\n");
267     consider_restart (event->status);
268     GNUNET_assert (download == NULL);
269     download = event->value.download.dc;
270     GNUNET_assert (0 == strcmp ("download", event->value.download.cctx));
271     GNUNET_assert (NULL == event->value.download.pctx);
272     GNUNET_assert (NULL != event->value.download.uri);
273     GNUNET_assert (0 == strcmp (fn, event->value.download.filename));
274     GNUNET_assert (FILESIZE == event->value.download.size);
275     GNUNET_assert (0 == event->value.download.completed);
276     GNUNET_assert (1 == event->value.download.anonymity);
277     break;
278   case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
279     GNUNET_assert (download == event->value.download.dc);
280     GNUNET_SCHEDULER_add_continuation (&abort_publish_task, NULL,
281                                        GNUNET_SCHEDULER_REASON_PREREQ_DONE);
282     download = NULL;
283     break;
284   default:
285     printf ("Unexpected event: %d\n", event->status);
286     break;
287   }
288   return NULL;
289 }
290
291
292 static void
293 setup_peer (struct PeerContext *p, const char *cfgname)
294 {
295   p->cfg = GNUNET_CONFIGURATION_create ();
296 #if START_ARM
297   p->arm_proc =
298       GNUNET_OS_start_process (NULL, NULL, "gnunet-service-arm",
299                                "gnunet-service-arm",
300 #if VERBOSE
301                                "-L", "DEBUG",
302 #endif
303                                "-c", cfgname, NULL);
304 #endif
305   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
306 }
307
308
309 static void
310 stop_arm (struct PeerContext *p)
311 {
312 #if START_ARM
313   if (NULL != p->arm_proc)
314   {
315     if (0 != GNUNET_OS_process_kill (p->arm_proc, SIGTERM))
316       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
317     if (GNUNET_OS_process_wait (p->arm_proc) != GNUNET_OK)
318       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
319     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM process %u stopped\n",
320                 GNUNET_OS_process_get_pid (p->arm_proc));
321     GNUNET_OS_process_close (p->arm_proc);
322     p->arm_proc = NULL;
323   }
324 #endif
325   GNUNET_CONFIGURATION_destroy (p->cfg);
326 }
327
328
329 static void
330 run (void *cls, char *const *args, const char *cfgfile,
331      const struct GNUNET_CONFIGURATION_Handle *c)
332 {
333   const char *keywords[] = {
334     "down_foo",
335     "down_bar",
336   };
337   char *buf;
338   struct GNUNET_CONTAINER_MetaData *meta;
339   struct GNUNET_FS_Uri *kuri;
340   struct GNUNET_FS_FileInformation *fi;
341   size_t i;
342   struct GNUNET_FS_BlockOptions bo;
343
344   cfg = c;
345   setup_peer (&p1, "test_fs_download_data.conf");
346   fs = GNUNET_FS_start (cfg, "test-fs-download-persistence", &progress_cb, NULL,
347                         GNUNET_FS_FLAGS_PERSISTENCE, GNUNET_FS_OPTIONS_END);
348   GNUNET_assert (NULL != fs);
349   buf = GNUNET_malloc (FILESIZE);
350   for (i = 0; i < FILESIZE; i++)
351     buf[i] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 256);
352   meta = GNUNET_CONTAINER_meta_data_create ();
353   kuri = GNUNET_FS_uri_ksk_create_from_args (2, keywords);
354   bo.content_priority = 42;
355   bo.anonymity_level = 1;
356   bo.replication_level = 0;
357   bo.expiration_time = GNUNET_TIME_relative_to_absolute (LIFETIME);
358   fi = GNUNET_FS_file_information_create_from_data (fs, "publish-context",
359                                                     FILESIZE, buf, kuri, meta,
360                                                     GNUNET_NO, &bo);
361   GNUNET_FS_uri_destroy (kuri);
362   GNUNET_CONTAINER_meta_data_destroy (meta);
363   GNUNET_assert (NULL != fi);
364   timeout_kill =
365       GNUNET_SCHEDULER_add_delayed (TIMEOUT, &timeout_kill_task, NULL);
366   start = GNUNET_TIME_absolute_get ();
367   publish =
368       GNUNET_FS_publish_start (fs, fi, NULL, NULL, NULL,
369                                GNUNET_FS_PUBLISH_OPTION_NONE);
370   GNUNET_assert (publish != NULL);
371 }
372
373
374 int
375 main (int argc, char *argv[])
376 {
377   char *const argvx[] = {
378     "test-fs-download-persistence",
379     "-c",
380     "test_fs_download_data.conf",
381 #if VERBOSE
382     "-L", "DEBUG",
383 #endif
384     NULL
385   };
386   struct GNUNET_GETOPT_CommandLineOption options[] = {
387     GNUNET_GETOPT_OPTION_END
388   };
389   GNUNET_log_setup ("test_fs_download_persistence",
390 #if VERBOSE
391                     "DEBUG",
392 #else
393                     "WARNING",
394 #endif
395                     NULL);
396   GNUNET_DISK_directory_remove ("/tmp/gnunet-test-fs-download/");
397   GNUNET_PROGRAM_run ((sizeof (argvx) / sizeof (char *)) - 1, argvx,
398                       "test-fs-download-persistence", "nohelp", options, &run,
399                       NULL);
400   stop_arm (&p1);
401   GNUNET_DISK_directory_remove ("/tmp/gnunet-test-fs-download/");
402   return err;
403 }
404
405 /* end of test_fs_download_persistence.c */