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