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