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