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