8306163693edc830ec24e16daf82b0cd3a3cd661
[oweals/gnunet.git] / src / datastore / test_plugin_datastore.c
1 /*
2      This file is part of GNUnet.
3      (C) 2004, 2005, 2006, 2007, 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 test_plugin_datastore.c
22  * @brief Profile database plugin directly, focusing on iterators.
23  * @author Christian Grothoff
24  */
25
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_protocols.h"
29 #include "gnunet_datastore_plugin.h"
30
31 #define VERBOSE GNUNET_NO
32
33 /**
34  * Target datastore size (in bytes).  Realistic sizes are
35  * more like 16 GB (not the default of 16 MB); however,
36  * those take too long to run them in the usual "make check"
37  * sequence.  Hence the value used for shipping is tiny.
38  */
39 #define MAX_SIZE 1024LL * 1024 * 128
40
41 #define ITERATIONS 2
42
43 /**
44  * Number of put operations to perform.
45  */
46 #define PUT_10 10
47
48 static unsigned long long stored_bytes;
49
50 static unsigned long long stored_entries;
51
52 static unsigned long long stored_ops;
53
54 static const char *plugin_name;
55
56 static int ok;
57
58 enum RunPhase
59   {
60     RP_ERROR = 0,
61     RP_PUT,
62     RP_GET,
63     RP_UPDATE,
64     RP_ITER_ZERO,
65     RP_REPL_GET,
66     RP_EXPI_GET,
67     RP_DROP
68   };
69
70
71 struct CpsRunContext
72 {
73   const struct GNUNET_CONFIGURATION_Handle *cfg;
74   struct GNUNET_DATASTORE_PluginFunctions * api;
75   enum RunPhase phase;
76   unsigned int cnt;
77   unsigned int i;
78 };
79
80
81 /**
82  * Function called by plugins to notify us about a
83  * change in their disk utilization.
84  *
85  * @param cls closure (NULL)
86  * @param delta change in disk utilization, 
87  *        0 for "reset to empty"
88  */
89 static void
90 disk_utilization_change_cb (void *cls,
91                             int delta)
92 {
93   /* do nothing */
94 }
95
96
97 static void
98 gen_key (int i,
99          GNUNET_HashCode *key)
100 {
101   memset (key, 0, sizeof (GNUNET_HashCode));
102   key->bits[0] = (unsigned int) i;
103   GNUNET_CRYPTO_hash (key, sizeof (GNUNET_HashCode), key);
104 }
105
106              
107 static void
108 put_value (struct GNUNET_DATASTORE_PluginFunctions * api,
109            int i, 
110            int k)
111 {
112   char value[65536];
113   size_t size;
114   GNUNET_HashCode key;
115   char *msg;
116   unsigned int prio;
117
118   /* most content is 32k */
119   size = 32 * 1024;
120
121   if (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 16) == 0)  /* but some of it is less! */
122     size = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 32 * 1024);
123   size = size - (size & 7);     /* always multiple of 8 */
124
125   /* generate random key */
126   gen_key (i, &key);
127   memset (value, i, size);
128   if (i > 255)
129     memset (value, i - 255, size / 2);
130   value[0] = k;
131   msg = NULL;
132   prio = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 100);
133   if (GNUNET_OK != api->put (api->cls,
134                              &key, 
135                              size,
136                              value,
137                              i + 1 /* type */,
138                              prio,
139                              i /* anonymity */,
140                              0 /* replication */,
141                              GNUNET_TIME_relative_to_absolute 
142                              (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
143                                                              60 * 60 * 60 * 1000 +
144                                                              GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 1000))),
145                              &msg))
146     {
147       fprintf (stderr, "ERROR: `%s'\n", msg);
148       GNUNET_free_non_null (msg);
149       return;
150     }
151   stored_bytes += size;
152   stored_ops++;
153   stored_entries++;
154 }
155
156
157 static void
158 test (void *cls,
159       const struct GNUNET_SCHEDULER_TaskContext *tc);
160
161
162 static int
163 iterate_one_shot (void *cls,
164                   void *next_cls,
165                   const GNUNET_HashCode * key,
166                   uint32_t size,
167                   const void *data,
168                   enum GNUNET_BLOCK_Type type,
169                   uint32_t priority,
170                   uint32_t anonymity,
171                   struct GNUNET_TIME_Absolute
172                   expiration, 
173                   uint64_t uid)
174 {
175   struct CpsRunContext *crc = cls;
176   
177   GNUNET_assert (NULL == next_cls);
178   GNUNET_assert (key != NULL);
179   crc->phase++;
180 #if VERBOSE
181   fprintf (stderr,
182            "Found result type=%u, priority=%u, size=%u, expire=%llu\n",
183            type, priority, size,
184            (unsigned long long) expiration.abs_value);
185 #endif    
186   GNUNET_SCHEDULER_add_now (&test, crc);
187   return GNUNET_OK;
188 }
189
190
191 static uint64_t guid;
192
193 static int
194 iterate_with_next (void *cls,
195                    void *next_cls,
196                    const GNUNET_HashCode * key,
197                    uint32_t size,
198                    const void *data,
199                    enum GNUNET_BLOCK_Type type,
200                    uint32_t priority,
201                    uint32_t anonymity,
202                    struct GNUNET_TIME_Absolute
203                    expiration, 
204                    uint64_t uid)
205 {
206   struct CpsRunContext *crc = cls;
207   
208   if (key == NULL)
209     {
210       crc->phase++;
211       GNUNET_SCHEDULER_add_now (&test, crc);
212       return GNUNET_OK;
213     }
214   guid = uid;
215 #if VERBOSE
216   fprintf (stderr,
217            "Found result type=%u, priority=%u, size=%u, expire=%llu\n",
218            type, priority, size,
219            (unsigned long long) expiration.abs_value);
220 #endif
221   crc->cnt++;
222   crc->api->next_request (next_cls,
223                           GNUNET_NO);
224   return GNUNET_OK;
225 }
226
227
228 /**
229  * Function called when the service shuts
230  * down.  Unloads our datastore plugin.
231  *
232  * @param api api to unload
233  * @param cfg configuration to use
234  */
235 static void
236 unload_plugin (struct GNUNET_DATASTORE_PluginFunctions * api,
237                const struct GNUNET_CONFIGURATION_Handle *cfg)
238 {
239   char *name;
240   char *libname;
241
242   if (GNUNET_OK !=
243       GNUNET_CONFIGURATION_get_value_string (cfg,
244                                              "DATASTORE", "DATABASE", &name))
245     {
246       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
247                   _("No `%s' specified for `%s' in configuration!\n"),
248                   "DATABASE",
249                   "DATASTORE");
250       return;
251     }
252   GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
253   GNUNET_break (NULL == GNUNET_PLUGIN_unload (libname, api));
254   GNUNET_free (libname);
255   GNUNET_free (name);
256 }
257
258
259
260 /**
261  * Last task run during shutdown.  Disconnects us from
262  * the transport and core.
263  */
264 static void
265 cleaning_task (void *cls,
266                const struct GNUNET_SCHEDULER_TaskContext *tc)
267 {
268   struct CpsRunContext *crc = cls;
269
270   unload_plugin (crc->api, crc->cfg);
271   GNUNET_free (crc);
272 }
273
274
275 static void
276 test (void *cls,
277       const struct GNUNET_SCHEDULER_TaskContext *tc)
278 {  
279   struct CpsRunContext *crc = cls;
280   int j;
281   unsigned long long os;
282   unsigned long long cs;
283   GNUNET_HashCode key;
284
285   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
286     {
287       crc->phase = RP_ERROR;
288       ok = 1;
289     }
290   switch (crc->phase)
291     {
292     case RP_ERROR:
293       GNUNET_break (0); 
294       crc->api->drop (crc->api->cls);
295       GNUNET_SCHEDULER_add_now (&cleaning_task, crc);
296       break;
297     case RP_PUT:      
298       os = 0;
299       for (j=0;j<PUT_10;j++)
300         {
301           put_value (crc->api, j, crc->i);
302           cs = crc->api->get_size (crc->api->cls);
303           GNUNET_assert (os < cs);
304           os = cs;
305         }
306       crc->phase++;
307       GNUNET_SCHEDULER_add_now (&test, crc);
308       break;
309     case RP_GET:
310       if (crc->cnt == 1)
311         {
312           crc->cnt = 0;
313           crc->phase++;
314           GNUNET_SCHEDULER_add_now (&test, crc);
315           break;
316         }
317       gen_key (5, &key);
318       crc->api->get (crc->api->cls,
319                      &key, NULL, 
320                      GNUNET_BLOCK_TYPE_ANY,
321                      &iterate_with_next,
322                      crc);
323       break;
324     case RP_UPDATE:
325       GNUNET_assert (GNUNET_OK ==
326                      crc->api->update (crc->api->cls,
327                                        guid, 1, 
328                                        GNUNET_TIME_UNIT_ZERO_ABS,
329                                        NULL));
330       crc->phase++;
331       GNUNET_SCHEDULER_add_now (&test, crc);
332       break;
333
334     case RP_ITER_ZERO:
335       if (crc->cnt == 1)
336         {
337           crc->cnt = 0;
338           crc->phase++;
339           GNUNET_SCHEDULER_add_now (&test, crc);
340           break;
341         }
342       crc->api->iter_zero_anonymity (crc->api->cls, 
343                                      1, 
344                                      &iterate_with_next,
345                                      crc);
346       break;
347     case RP_REPL_GET:
348       crc->api->replication_get (crc->api->cls, 
349                                  &iterate_one_shot,
350                                  crc);
351       break;
352     case RP_EXPI_GET:
353       crc->api->expiration_get (crc->api->cls, 
354                                 &iterate_one_shot,
355                                 crc);
356       break;
357     case RP_DROP:
358       crc->api->drop (crc->api->cls);
359       GNUNET_SCHEDULER_add_now (&cleaning_task, crc);
360       break;
361     }
362 }
363
364
365 /**
366  * Load the datastore plugin.
367  */
368 static struct GNUNET_DATASTORE_PluginFunctions *
369 load_plugin (const struct GNUNET_CONFIGURATION_Handle *cfg)
370 {
371   static struct GNUNET_DATASTORE_PluginEnvironment env;
372   struct GNUNET_DATASTORE_PluginFunctions * ret; 
373   char *name;
374   char *libname;
375
376   if (GNUNET_OK !=
377       GNUNET_CONFIGURATION_get_value_string (cfg,
378                                              "DATASTORE", "DATABASE", &name))
379     {
380       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
381                   _("No `%s' specified for `%s' in configuration!\n"),
382                   "DATABASE",
383                   "DATASTORE");
384       return NULL;
385     }
386   env.cfg = cfg;
387   env.duc = &disk_utilization_change_cb;
388   env.cls = NULL;
389   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
390               _("Loading `%s' datastore plugin\n"), name);
391   GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
392   if (NULL == (ret = GNUNET_PLUGIN_load (libname, &env)))
393     {
394       fprintf (stderr,
395                "Failed to load plugin `%s'!\n",
396                name);
397       return NULL;
398     }
399   GNUNET_free (libname);
400   GNUNET_free (name);
401   return ret;
402 }
403
404
405 static void
406 run (void *cls,
407      char *const *args,
408      const char *cfgfile,
409      const struct GNUNET_CONFIGURATION_Handle *c)
410 {
411   struct GNUNET_DATASTORE_PluginFunctions *api;
412   struct CpsRunContext *crc;
413
414   api = load_plugin (c);
415   if (api == NULL)
416     {
417       fprintf (stderr, 
418                "Could not initialize plugin, assuming database not configured. Test not run!\n");
419       return;
420     }
421   crc = GNUNET_malloc(sizeof(struct CpsRunContext));
422   crc->api = api;
423   crc->cfg = c;
424   crc->phase = RP_PUT;
425   GNUNET_SCHEDULER_add_now (&test, crc);
426 }
427
428
429 static int
430 check ()
431 {
432   char cfg_name[128];
433   char *const argv[] = { 
434     "test-plugin-datastore",
435     "-c",
436     cfg_name,
437 #if VERBOSE
438     "-L", "DEBUG",
439 #endif
440     NULL
441   };
442   struct GNUNET_GETOPT_CommandLineOption options[] = {
443     GNUNET_GETOPT_OPTION_END
444   };
445
446   GNUNET_snprintf (cfg_name,
447                    sizeof (cfg_name),
448                    "test_plugin_datastore_data_%s.conf",
449                    plugin_name);
450   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
451                       argv, "test-plugin-datastore", "nohelp",
452                       options, &run, NULL);
453   if (ok != 0)
454     fprintf (stderr, "Missed some testcases: %u\n", ok);
455   return ok;
456 }
457
458
459 int
460 main (int argc, char *argv[])
461 {
462   int ret;
463   char *pos;
464   char dir_name[128];
465
466   /* determine name of plugin to use */
467   plugin_name = argv[0];
468   while (NULL != (pos = strstr(plugin_name, "_")))
469     plugin_name = pos+1;
470   if (NULL != (pos = strstr(plugin_name, ".")))
471     pos[0] = 0;
472   else
473     pos = (char *) plugin_name;
474
475   GNUNET_snprintf (dir_name,
476                    sizeof (dir_name),
477                    "/tmp/test-gnunet-datastore-%s",
478                    plugin_name);
479   GNUNET_DISK_directory_remove (dir_name);
480   GNUNET_log_setup ("test-plugin-datastore",
481 #if VERBOSE
482                     "DEBUG",
483 #else
484                     "WARNING",
485 #endif
486                     NULL);
487   ret = check ();
488   if (pos != plugin_name)
489     pos[0] = '.';
490   GNUNET_DISK_directory_remove (dir_name);
491
492   return ret;
493 }
494
495 /* end of test_plugin_datastore.c */
496
497