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