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