src: for every AGPL3.0 file, add SPDX identifier.
[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) == 0)   /* but some of it is less! */
158     size = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 32 * 1024);
159   size = size - (size & 7);     /* always multiple of 8 */
160
161   /* generate random key */
162   gen_key (i, &key);
163   memset (value, i, size);
164   if (i > 255)
165     memset (value, i - 255, size / 2);
166   value[0] = crc->i;
167   prio = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 100);
168   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
169               "putting type %u, anon %u under key %s\n", i + 1, i,
170               GNUNET_h2s (&key));
171   crc->api->put (crc->api->cls,
172                  &key,
173                  false /* absent */,
174                  size,
175                  value, i + 1 /* type */ ,
176                  prio,
177                  i /* anonymity */ ,
178                  0 /* replication */ ,
179                  GNUNET_TIME_relative_to_absolute
180                    (GNUNET_TIME_relative_multiply
181                      (GNUNET_TIME_UNIT_MILLISECONDS,
182                       60 * 60 * 60 * 1000 +
183                       GNUNET_CRYPTO_random_u32
184                       (GNUNET_CRYPTO_QUALITY_WEAK, 1000))),
185                  put_continuation,
186                  crc);
187   i++;
188 }
189
190
191 static uint64_t guid;
192
193
194 static int
195 iterate_one_shot (void *cls,
196                   const struct GNUNET_HashCode *key,
197                   uint32_t size,
198                   const void *data,
199                   enum GNUNET_BLOCK_Type type,
200                   uint32_t priority,
201                   uint32_t anonymity,
202                   uint32_t replication,
203                   struct GNUNET_TIME_Absolute expiration,
204                   uint64_t uid)
205 {
206   struct CpsRunContext *crc = cls;
207
208   GNUNET_assert (NULL != key);
209   guid = uid;
210   crc->phase++;
211   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
212               "Found result type=%u, priority=%u, size=%u, expire=%s, key %s\n",
213               (unsigned int) type,
214               (unsigned int) priority,
215               (unsigned int) size,
216               GNUNET_STRINGS_absolute_time_to_string (expiration),
217               GNUNET_h2s (key));
218   GNUNET_SCHEDULER_add_now (&test,
219                             crc);
220   return GNUNET_OK;
221 }
222
223
224 static void
225 remove_continuation (void *cls,
226                      const struct GNUNET_HashCode *key,
227                      uint32_t size,
228                      int status,
229                      const char *msg)
230 {
231   struct CpsRunContext *crc = cls;
232
233   GNUNET_assert (NULL != key);
234   GNUNET_assert (32768 == size);
235   GNUNET_assert (GNUNET_OK == status);
236   GNUNET_assert (NULL == msg);
237   crc->phase++;
238   GNUNET_SCHEDULER_add_now (&test,
239                             crc);
240 }
241
242
243 /**
244  * Function called when the service shuts
245  * down.  Unloads our datastore plugin.
246  *
247  * @param api api to unload
248  * @param cfg configuration to use
249  */
250 static void
251 unload_plugin (struct GNUNET_DATASTORE_PluginFunctions *api,
252                const struct GNUNET_CONFIGURATION_Handle *cfg)
253 {
254   char *name;
255   char *libname;
256
257   if (GNUNET_OK !=
258       GNUNET_CONFIGURATION_get_value_string (cfg,
259                                              "DATASTORE",
260                                              "DATABASE",
261                                              &name))
262   {
263     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
264                 _("No `%s' specified for `%s' in configuration!\n"),
265                 "DATABASE",
266                 "DATASTORE");
267     return;
268   }
269   GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
270   GNUNET_break (NULL == GNUNET_PLUGIN_unload (libname, api));
271   GNUNET_free (libname);
272   GNUNET_free (name);
273 }
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   case RP_PUT:
308     do_put (crc);
309     break;
310   case RP_GET:
311     if (crc->cnt == 1)
312     {
313       crc->cnt = 0;
314       crc->phase++;
315       GNUNET_SCHEDULER_add_now (&test, crc);
316       break;
317     }
318     gen_key (5, &key);
319     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
320                 "Looking for %s\n",
321                 GNUNET_h2s (&key));
322     crc->api->get_key (crc->api->cls,
323                        0,
324                        false,
325                        &key,
326                        GNUNET_BLOCK_TYPE_ANY,
327                        &iterate_one_shot,
328                        crc);
329     break;
330   case RP_ITER_ZERO:
331     if (crc->cnt == 1)
332     {
333       crc->cnt = 0;
334       crc->phase++;
335       GNUNET_SCHEDULER_add_now (&test, crc);
336       break;
337     }
338     crc->api->get_zero_anonymity (crc->api->cls, 0, 1, &iterate_one_shot, crc);
339     break;
340   case RP_REPL_GET:
341     crc->api->get_replication (crc->api->cls, &iterate_one_shot, crc);
342     break;
343   case RP_EXPI_GET:
344     crc->api->get_expiration (crc->api->cls, &iterate_one_shot, crc);
345     break;
346   case RP_REMOVE:
347     {
348       struct GNUNET_HashCode key;
349       uint32_t size = 32768;
350       char value[size];
351
352       gen_key (0, &key);
353       memset (value, 0, size);
354       value[0] = crc->i;
355       crc->api->remove_key (crc->api->cls,
356                             &key,
357                             size,
358                             value,
359                             &remove_continuation,
360                             crc);
361       break;
362     }
363   case RP_DROP:
364     crc->api->drop (crc->api->cls);
365     GNUNET_SCHEDULER_add_now (&cleaning_task, crc);
366     break;
367   }
368 }
369
370
371 /**
372  * Load the datastore plugin.
373  */
374 static struct GNUNET_DATASTORE_PluginFunctions *
375 load_plugin (const struct GNUNET_CONFIGURATION_Handle *cfg)
376 {
377   static struct GNUNET_DATASTORE_PluginEnvironment env;
378   struct GNUNET_DATASTORE_PluginFunctions *ret;
379   char *name;
380   char *libname;
381
382   if (GNUNET_OK !=
383       GNUNET_CONFIGURATION_get_value_string (cfg,
384                                              "DATASTORE",
385                                              "DATABASE",
386                                              &name))
387   {
388     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
389                 _("No `%s' specified for `%s' in configuration!\n"),
390                 "DATABASE",
391                 "DATASTORE");
392     return NULL;
393   }
394   env.cfg = cfg;
395   env.duc = &disk_utilization_change_cb;
396   env.cls = NULL;
397   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Loading `%s' datastore plugin\n"),
398               name);
399   GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
400   if (NULL == (ret = GNUNET_PLUGIN_load (libname, &env)))
401   {
402     FPRINTF (stderr, "Failed to load plugin `%s'!\n", name);
403     GNUNET_free (libname);
404     GNUNET_free (name);
405     ok = 77; /* mark test as skipped */
406     return NULL;
407   }
408   GNUNET_free (libname);
409   GNUNET_free (name);
410   return ret;
411 }
412
413
414 static void
415 run (void *cls, char *const *args, const char *cfgfile,
416      const struct GNUNET_CONFIGURATION_Handle *c)
417 {
418   struct GNUNET_DATASTORE_PluginFunctions *api;
419   struct CpsRunContext *crc;
420
421   api = load_plugin (c);
422   if (api == NULL)
423   {
424     FPRINTF (stderr,
425              "%s", "Could not initialize plugin, assuming database not configured. Test not run!\n");
426     return;
427   }
428   crc = GNUNET_new (struct CpsRunContext);
429   crc->api = api;
430   crc->cfg = c;
431   crc->phase = RP_PUT;
432   GNUNET_SCHEDULER_add_now (&test, crc);
433 }
434
435
436 int
437 main (int argc, char *argv[])
438 {
439   char dir_name[128];
440   char cfg_name[128];
441   char *const xargv[] = {
442     "test-plugin-datastore",
443     "-c",
444     cfg_name,
445     NULL
446   };
447   static struct GNUNET_GETOPT_CommandLineOption options[] = {
448     GNUNET_GETOPT_OPTION_END
449   };
450
451   /* determine name of plugin to use */
452   plugin_name = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
453   GNUNET_snprintf (dir_name, sizeof (dir_name),
454                    "/tmp/test-gnunet-datastore-plugin-%s", plugin_name);
455   GNUNET_DISK_directory_remove (dir_name);
456   GNUNET_log_setup ("test-plugin-datastore",
457                     "WARNING",
458                     NULL);
459   GNUNET_snprintf (cfg_name, sizeof (cfg_name),
460                    "test_plugin_datastore_data_%s.conf", plugin_name);
461   GNUNET_PROGRAM_run ((sizeof (xargv) / sizeof (char *)) - 1, xargv,
462                       "test-plugin-datastore", "nohelp", options, &run, NULL);
463   if ( (0 != ok) && (77 != ok) )
464     FPRINTF (stderr, "Missed some testcases: %u\n", ok);
465   GNUNET_DISK_directory_remove (dir_name);
466   return ok;
467 }
468
469 /* end of test_plugin_datastore.c */