use NULL value in load_path_suffix to NOT load any files
[oweals/gnunet.git] / src / datastore / test_plugin_datastore.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2011 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
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_ITER_ZERO,
53   RP_REPL_GET,
54   RP_EXPI_GET,
55   RP_REMOVE,
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 };
68
69
70 /**
71  * Function called by plugins to notify us about a
72  * change in their disk utilization.
73  *
74  * @param cls closure (NULL)
75  * @param delta change in disk utilization,
76  *        0 for "reset to empty"
77  */
78 static void
79 disk_utilization_change_cb (void *cls, int delta)
80 {
81   /* do nothing */
82 }
83
84
85 static void
86 test (void *cls);
87
88
89 /**
90  * Put continuation.
91  *
92  * @param cls closure
93  * @param key key for the item stored
94  * @param size size of the item stored
95  * @param status #GNUNET_OK or #GNUNET_SYSERROR
96  * @param msg error message on error
97  */
98 static void
99 put_continuation (void *cls,
100                   const struct GNUNET_HashCode *key,
101                   uint32_t size,
102                   int status,
103                   const char *msg)
104 {
105   struct CpsRunContext *crc = cls;
106   static unsigned long long os;
107   unsigned long long cs;
108
109   if (GNUNET_OK != status)
110   {
111     fprintf (stderr,
112              "ERROR: `%s'\n",
113              msg);
114   }
115   else
116   {
117     crc->api->estimate_size (crc->api->cls,
118                              &cs);
119     GNUNET_assert (os <= cs);
120     os = cs;
121     stored_bytes += size;
122     stored_ops++;
123     stored_entries++;
124   }
125   GNUNET_SCHEDULER_add_now (&test, crc);
126 }
127
128
129 static void
130 gen_key (int i, struct GNUNET_HashCode *key)
131 {
132   memset (key, 0, sizeof(struct GNUNET_HashCode));
133   key->bits[0] = (unsigned int) i;
134   GNUNET_CRYPTO_hash (key, sizeof(struct GNUNET_HashCode), key);
135 }
136
137
138 static void
139 do_put (struct CpsRunContext *crc)
140 {
141   char value[65536];
142   size_t size;
143   struct GNUNET_HashCode key;
144   unsigned int prio;
145   static int i;
146
147   if (PUT_10 == i)
148   {
149     i = 0;
150     crc->phase++;
151     GNUNET_SCHEDULER_add_now (&test, crc);
152     return;
153   }
154   /* most content is 32k */
155   size = 32 * 1024;
156
157   if ((0 != i) && (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 16) ==
158                    0) )                                                           /* but some of it is less! */
159     size = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 32 * 1024);
160   size = size - (size & 7);     /* always multiple of 8 */
161
162   /* generate random key */
163   gen_key (i, &key);
164   memset (value, i, size);
165   if (i > 255)
166     memset (value, i - 255, size / 2);
167   value[0] = crc->i;
168   prio = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 100);
169   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
170               "putting type %u, anon %u under key %s\n", i + 1, i,
171               GNUNET_h2s (&key));
172   crc->api->put (crc->api->cls,
173                  &key,
174                  false /* absent */,
175                  size,
176                  value, i + 1 /* type */,
177                  prio,
178                  i /* anonymity */,
179                  0 /* replication */,
180                  GNUNET_TIME_relative_to_absolute
181                    (GNUNET_TIME_relative_multiply
182                      (GNUNET_TIME_UNIT_MILLISECONDS,
183                      60 * 60 * 60 * 1000
184                      + GNUNET_CRYPTO_random_u32
185                        (GNUNET_CRYPTO_QUALITY_WEAK, 1000))),
186                  put_continuation,
187                  crc);
188   i++;
189 }
190
191
192 static uint64_t guid;
193
194
195 static int
196 iterate_one_shot (void *cls,
197                   const struct GNUNET_HashCode *key,
198                   uint32_t size,
199                   const void *data,
200                   enum GNUNET_BLOCK_Type type,
201                   uint32_t priority,
202                   uint32_t anonymity,
203                   uint32_t replication,
204                   struct GNUNET_TIME_Absolute expiration,
205                   uint64_t uid)
206 {
207   struct CpsRunContext *crc = cls;
208
209   GNUNET_assert (NULL != key);
210   guid = uid;
211   crc->phase++;
212   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
213               "Found result type=%u, priority=%u, size=%u, expire=%s, key %s\n",
214               (unsigned int) type,
215               (unsigned int) priority,
216               (unsigned int) size,
217               GNUNET_STRINGS_absolute_time_to_string (expiration),
218               GNUNET_h2s (key));
219   GNUNET_SCHEDULER_add_now (&test,
220                             crc);
221   return GNUNET_OK;
222 }
223
224
225 static void
226 remove_continuation (void *cls,
227                      const struct GNUNET_HashCode *key,
228                      uint32_t size,
229                      int status,
230                      const char *msg)
231 {
232   struct CpsRunContext *crc = cls;
233
234   GNUNET_assert (NULL != key);
235   GNUNET_assert (32768 == size);
236   GNUNET_assert (GNUNET_OK == status);
237   GNUNET_assert (NULL == msg);
238   crc->phase++;
239   GNUNET_SCHEDULER_add_now (&test,
240                             crc);
241 }
242
243
244 /**
245  * Function called when the service shuts
246  * down.  Unloads our datastore plugin.
247  *
248  * @param api api to unload
249  * @param cfg configuration to use
250  */
251 static void
252 unload_plugin (struct GNUNET_DATASTORE_PluginFunctions *api,
253                const struct GNUNET_CONFIGURATION_Handle *cfg)
254 {
255   char *name;
256   char *libname;
257
258   if (GNUNET_OK !=
259       GNUNET_CONFIGURATION_get_value_string (cfg,
260                                              "DATASTORE",
261                                              "DATABASE",
262                                              &name))
263   {
264     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
265                 _ ("No `%s' specified for `%s' in configuration!\n"),
266                 "DATABASE",
267                 "DATASTORE");
268     return;
269   }
270   GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
271   GNUNET_break (NULL == GNUNET_PLUGIN_unload (libname, api));
272   GNUNET_free (libname);
273   GNUNET_free (name);
274 }
275
276
277 /**
278  * Last task run during shutdown.  Disconnects us from
279  * the transport and core.
280  */
281 static void
282 cleaning_task (void *cls)
283 {
284   struct CpsRunContext *crc = cls;
285
286   unload_plugin (crc->api, crc->cfg);
287   GNUNET_free (crc);
288 }
289
290
291 static void
292 test (void *cls)
293 {
294   struct CpsRunContext *crc = cls;
295   struct GNUNET_HashCode key;
296
297   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
298               "In phase %d, iteration %u\n", crc->phase, crc->cnt);
299   switch (crc->phase)
300   {
301   case RP_ERROR:
302     ok = 1;
303     GNUNET_break (0);
304     crc->api->drop (crc->api->cls);
305     GNUNET_SCHEDULER_add_now (&cleaning_task, crc);
306     break;
307
308   case RP_PUT:
309     do_put (crc);
310     break;
311
312   case RP_GET:
313     if (crc->cnt == 1)
314     {
315       crc->cnt = 0;
316       crc->phase++;
317       GNUNET_SCHEDULER_add_now (&test, crc);
318       break;
319     }
320     gen_key (5, &key);
321     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
322                 "Looking for %s\n",
323                 GNUNET_h2s (&key));
324     crc->api->get_key (crc->api->cls,
325                        0,
326                        false,
327                        &key,
328                        GNUNET_BLOCK_TYPE_ANY,
329                        &iterate_one_shot,
330                        crc);
331     break;
332
333   case RP_ITER_ZERO:
334     if (crc->cnt == 1)
335     {
336       crc->cnt = 0;
337       crc->phase++;
338       GNUNET_SCHEDULER_add_now (&test, crc);
339       break;
340     }
341     crc->api->get_zero_anonymity (crc->api->cls, 0, 1, &iterate_one_shot, crc);
342     break;
343
344   case RP_REPL_GET:
345     crc->api->get_replication (crc->api->cls, &iterate_one_shot, crc);
346     break;
347
348   case RP_EXPI_GET:
349     crc->api->get_expiration (crc->api->cls, &iterate_one_shot, crc);
350     break;
351
352   case RP_REMOVE:
353     {
354       struct GNUNET_HashCode key;
355       uint32_t size = 32768;
356       char value[size];
357
358       gen_key (0, &key);
359       memset (value, 0, size);
360       value[0] = crc->i;
361       crc->api->remove_key (crc->api->cls,
362                             &key,
363                             size,
364                             value,
365                             &remove_continuation,
366                             crc);
367       break;
368     }
369
370   case RP_DROP:
371     crc->api->drop (crc->api->cls);
372     GNUNET_SCHEDULER_add_now (&cleaning_task, crc);
373     break;
374   }
375 }
376
377
378 /**
379  * Load the datastore plugin.
380  */
381 static struct GNUNET_DATASTORE_PluginFunctions *
382 load_plugin (const struct GNUNET_CONFIGURATION_Handle *cfg)
383 {
384   static struct GNUNET_DATASTORE_PluginEnvironment env;
385   struct GNUNET_DATASTORE_PluginFunctions *ret;
386   char *name;
387   char *libname;
388
389   if (GNUNET_OK !=
390       GNUNET_CONFIGURATION_get_value_string (cfg,
391                                              "DATASTORE",
392                                              "DATABASE",
393                                              &name))
394   {
395     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
396                 _ ("No `%s' specified for `%s' in configuration!\n"),
397                 "DATABASE",
398                 "DATASTORE");
399     return NULL;
400   }
401   env.cfg = cfg;
402   env.duc = &disk_utilization_change_cb;
403   env.cls = NULL;
404   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Loading `%s' datastore plugin\n"),
405               name);
406   GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
407   if (NULL == (ret = GNUNET_PLUGIN_load (libname, &env)))
408   {
409     fprintf (stderr, "Failed to load plugin `%s'!\n", name);
410     GNUNET_free (libname);
411     GNUNET_free (name);
412     ok = 77;   /* mark test as skipped */
413     return NULL;
414   }
415   GNUNET_free (libname);
416   GNUNET_free (name);
417   return ret;
418 }
419
420
421 static void
422 run (void *cls, char *const *args, const char *cfgfile,
423      const struct GNUNET_CONFIGURATION_Handle *c)
424 {
425   struct GNUNET_DATASTORE_PluginFunctions *api;
426   struct CpsRunContext *crc;
427
428   api = load_plugin (c);
429   if (api == NULL)
430   {
431     fprintf (stderr,
432              "%s",
433              "Could not initialize plugin, assuming database not configured. Test not run!\n");
434     return;
435   }
436   crc = GNUNET_new (struct CpsRunContext);
437   crc->api = api;
438   crc->cfg = c;
439   crc->phase = RP_PUT;
440   GNUNET_SCHEDULER_add_now (&test, crc);
441 }
442
443
444 int
445 main (int argc, char *argv[])
446 {
447   char dir_name[PATH_MAX];
448   char cfg_name[PATH_MAX];
449   char *const xargv[] = {
450     "test-plugin-datastore",
451     "-c",
452     cfg_name,
453     NULL
454   };
455   static struct GNUNET_GETOPT_CommandLineOption options[] = {
456     GNUNET_GETOPT_OPTION_END
457   };
458
459   /* determine name of plugin to use */
460   plugin_name = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
461   GNUNET_snprintf (dir_name, sizeof(dir_name),
462                    "/tmp/test-gnunet-datastore-plugin-%s", plugin_name);
463   GNUNET_DISK_directory_remove (dir_name);
464   GNUNET_log_setup ("test-plugin-datastore",
465                     "WARNING",
466                     NULL);
467   GNUNET_snprintf (cfg_name, sizeof(cfg_name),
468                    "test_plugin_datastore_data_%s.conf", plugin_name);
469   GNUNET_PROGRAM_run ((sizeof(xargv) / sizeof(char *)) - 1, xargv,
470                       "test-plugin-datastore", "nohelp", options, &run, NULL);
471   if ((0 != ok) && (77 != ok))
472     fprintf (stderr, "Missed some testcases: %u\n", ok);
473   GNUNET_DISK_directory_remove (dir_name);
474   return ok;
475 }
476
477
478 /* end of test_plugin_datastore.c */