remove 'illegal' (non-reentrant) log logic from signal handler
[oweals/gnunet.git] / src / datastore / test_datastore_api.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2004, 2005, 2006, 2007, 2009, 2015 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 datastore/test_datastore_api.c
22  * @brief Test for the basic datastore API.
23  * @author Christian Grothoff
24  *
25  * TODO:
26  * - test reservation failure
27  */
28
29 #include "platform.h"
30 #include "gnunet_util_lib.h"
31 #include "gnunet_protocols.h"
32 #include "gnunet_datastore_service.h"
33 #include "gnunet_datastore_plugin.h"
34 #include "gnunet_testing_lib.h"
35
36
37 /**
38  * How long until we give up on transmitting the message?
39  */
40 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
41
42 #define ITERATIONS 256
43
44 /**
45  * Handle to the datastore.
46  */
47 static struct GNUNET_DATASTORE_Handle *datastore;
48
49 static struct GNUNET_TIME_Absolute now;
50
51 /**
52  * Value we return from #main().
53  */
54 static int ok;
55
56 /**
57  * Name of plugin under test.
58  */
59 static const char *plugin_name;
60
61
62 static size_t
63 get_size (int i)
64 {
65   return 8 * i;
66 }
67
68
69 static const void *
70 get_data (int i)
71 {
72   static char buf[60000];
73
74   memset (buf, i, 8 * i);
75   return buf;
76 }
77
78
79 static int
80 get_type (int i)
81 {
82   return i + 1;
83 }
84
85
86 static int
87 get_priority (int i)
88 {
89   return i + 1;
90 }
91
92
93 static int
94 get_anonymity (int i)
95 {
96   return i;
97 }
98
99
100 static struct GNUNET_TIME_Absolute
101 get_expiration (int i)
102 {
103   struct GNUNET_TIME_Absolute av;
104
105   av.abs_value_us = now.abs_value_us + 20000000000LL - i * 1000 * 1000LL;
106   return av;
107 }
108
109
110 /**
111  * Which phase of the process are we in?
112  */
113 enum RunPhase
114 {
115   /**
116    * We are done (shutting down normally).
117    */
118   RP_DONE = 0,
119
120   /**
121    * We are adding new entries to the datastore.
122    */
123   RP_PUT = 1,
124   RP_GET = 2,
125   RP_DEL = 3,
126   RP_DO_DEL = 4,
127   RP_DELVALIDATE = 5,
128   RP_RESERVE = 6,
129   RP_PUT_MULTIPLE = 7,
130   RP_PUT_MULTIPLE_NEXT = 8,
131   RP_GET_MULTIPLE = 9,
132   RP_GET_MULTIPLE_NEXT = 10,
133
134   /**
135    * Execution failed with some kind of error.
136    */
137   RP_ERROR
138 };
139
140
141 /**
142  * Closure we give to all of the functions executing the
143  * benchmark.  Could right now be global, but this allows
144  * us to theoretically run multiple clients "in parallel".
145  */
146 struct CpsRunContext
147 {
148   /**
149    * Execution phase we are in.
150    */
151   enum RunPhase phase;
152
153   struct GNUNET_HashCode key;
154   int i;
155   int rid;
156   void *data;
157   size_t size;
158
159   uint64_t first_uid;
160 };
161
162
163 /**
164  * Main state machine.  Executes the next step of the test
165  * depending on the current state.
166  *
167  * @param cls the `struct CpsRunContext`
168  */
169 static void
170 run_continuation (void *cls);
171
172
173 /**
174  * Continuation called to notify client about result of an
175  * operation.  Checks for errors, updates our iteration counters and
176  * continues execution with #run_continuation().
177  *
178  * @param cls the `struct CpsRunContext`
179  * @param success #GNUNET_SYSERR on failure
180  * @param min_expiration minimum expiration time required for content to be stored
181  *                by the datacache at this time, zero for unknown
182  * @param msg NULL on success, otherwise an error message
183  */
184 static void
185 check_success (void *cls,
186                int success,
187                struct GNUNET_TIME_Absolute min_expiration,
188                const char *msg)
189 {
190   struct CpsRunContext *crc = cls;
191
192   if (GNUNET_OK != success)
193   {
194     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
195                 "Operation %d/%d not successful: `%s'\n",
196                 crc->phase,
197                 crc->i,
198                 msg);
199     crc->phase = RP_ERROR;
200   }
201   GNUNET_free_non_null (crc->data);
202   crc->data = NULL;
203   GNUNET_SCHEDULER_add_now (&run_continuation, crc);
204 }
205
206
207 static void
208 get_reserved (void *cls,
209               int success,
210               struct GNUNET_TIME_Absolute min_expiration,
211               const char *msg)
212 {
213   struct CpsRunContext *crc = cls;
214
215   if (0 >= success)
216     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
217                 "Error obtaining reservation: `%s'\n",
218                 msg);
219   GNUNET_assert (0 < success);
220   crc->rid = success;
221   GNUNET_SCHEDULER_add_now (&run_continuation,
222                             crc);
223 }
224
225
226 static void
227 check_value (void *cls,
228              const struct GNUNET_HashCode *key,
229              size_t size,
230              const void *data,
231              enum GNUNET_BLOCK_Type type,
232              uint32_t priority,
233              uint32_t anonymity,
234              uint32_t replication,
235              struct GNUNET_TIME_Absolute expiration,
236              uint64_t uid)
237 {
238   struct CpsRunContext *crc = cls;
239   int i;
240
241   i = crc->i;
242   if (NULL == key)
243   {
244     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
245                 "Value check failed (got NULL key) in %d/%d\n",
246                 crc->phase,
247                 crc->i);
248     crc->phase = RP_ERROR;
249     GNUNET_SCHEDULER_add_now (&run_continuation,
250                               crc);
251     return;
252   }
253 #if 0
254   fprintf (stderr,
255            "Check value got `%s' of size %u, type %d, expire %s\n",
256            GNUNET_h2s (key), (unsigned int) size, type,
257            GNUNET_STRINGS_absolute_time_to_string (expiration));
258   fprintf (stderr,
259            "Check value iteration %d wants size %u, type %d, expire %s\n", i,
260            (unsigned int) get_size (i), get_type (i),
261            GNUNET_STRINGS_absolute_time_to_string (get_expiration (i)));
262 #endif
263   GNUNET_assert (size == get_size (i));
264   GNUNET_assert (0 == memcmp (data, get_data (i), size));
265   GNUNET_assert (type == get_type (i));
266   GNUNET_assert (priority == get_priority (i));
267   GNUNET_assert (anonymity == get_anonymity (i));
268   GNUNET_assert (expiration.abs_value_us == get_expiration (i).abs_value_us);
269   if (crc->i == 0)
270   {
271     crc->phase = RP_DEL;
272     crc->i = ITERATIONS;
273   }
274   GNUNET_SCHEDULER_add_now (&run_continuation,
275                             crc);
276 }
277
278
279 static void
280 delete_value (void *cls,
281               const struct GNUNET_HashCode *key,
282               size_t size,
283               const void *data,
284               enum GNUNET_BLOCK_Type type,
285               uint32_t priority,
286               uint32_t anonymity,
287               uint32_t replication,
288               struct GNUNET_TIME_Absolute expiration,
289               uint64_t uid)
290 {
291   struct CpsRunContext *crc = cls;
292
293   GNUNET_assert (NULL == crc->data);
294   GNUNET_assert (NULL != key);
295   crc->size = size;
296   crc->key = *key;
297   crc->data = GNUNET_malloc (size);
298   GNUNET_memcpy (crc->data, data, size);
299   crc->phase = RP_DO_DEL;
300   GNUNET_SCHEDULER_add_now (&run_continuation,
301                             crc);
302 }
303
304
305 static void
306 check_nothing (void *cls,
307                const struct GNUNET_HashCode *key,
308                size_t size,
309                const void *data,
310                enum GNUNET_BLOCK_Type type,
311                uint32_t priority,
312                uint32_t anonymity,
313                uint32_t replication,
314                struct GNUNET_TIME_Absolute expiration,
315                uint64_t uid)
316 {
317   struct CpsRunContext *crc = cls;
318
319   GNUNET_assert (key == NULL);
320   if (crc->i == 0)
321     crc->phase = RP_RESERVE;
322   GNUNET_SCHEDULER_add_now (&run_continuation,
323                             crc);
324 }
325
326
327 static void
328 check_multiple (void *cls,
329                 const struct GNUNET_HashCode *key,
330                 size_t size,
331                 const void *data,
332                 enum GNUNET_BLOCK_Type type,
333                 uint32_t priority,
334                 uint32_t anonymity,
335                 uint32_t replication,
336                 struct GNUNET_TIME_Absolute expiration,
337                 uint64_t uid)
338 {
339   struct CpsRunContext *crc = cls;
340
341   GNUNET_assert (key != NULL);
342   switch (crc->phase)
343   {
344   case RP_GET_MULTIPLE:
345     crc->phase = RP_GET_MULTIPLE_NEXT;
346     crc->first_uid = uid;
347     break;
348
349   case RP_GET_MULTIPLE_NEXT:
350     GNUNET_assert (uid != crc->first_uid);
351     crc->phase = RP_DONE;
352     break;
353
354   default:
355     GNUNET_break (0);
356     crc->phase = RP_ERROR;
357     break;
358   }
359   GNUNET_SCHEDULER_add_now (&run_continuation, crc);
360 }
361
362
363 /**
364  * Main state machine.  Executes the next step of the test
365  * depending on the current state.
366  *
367  * @param cls the `struct CpsRunContext`
368  */
369 static void
370 run_continuation (void *cls)
371 {
372   struct CpsRunContext *crc = cls;
373
374   ok = (int) crc->phase;
375   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
376               "Test in phase %u\n",
377               crc->phase);
378   switch (crc->phase)
379   {
380   case RP_PUT:
381     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
382                 "Executing PUT number %u\n",
383                 crc->i);
384     GNUNET_CRYPTO_hash (&crc->i, sizeof(int), &crc->key);
385     GNUNET_DATASTORE_put (datastore, 0, &crc->key, get_size (crc->i),
386                           get_data (crc->i), get_type (crc->i),
387                           get_priority (crc->i), get_anonymity (crc->i), 0,
388                           get_expiration (crc->i), 1, 1,
389                           &check_success, crc);
390     crc->i++;
391     if (crc->i == ITERATIONS)
392       crc->phase = RP_GET;
393     break;
394
395   case RP_GET:
396     crc->i--;
397     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
398                 "Executing GET number %u\n",
399                 crc->i);
400     GNUNET_CRYPTO_hash (&crc->i,
401                         sizeof(int),
402                         &crc->key);
403     GNUNET_DATASTORE_get_key (datastore,
404                               0,
405                               false,
406                               &crc->key,
407                               get_type (crc->i),
408                               1,
409                               1,
410                               &check_value,
411                               crc);
412     break;
413
414   case RP_DEL:
415     crc->i--;
416     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
417                 "Executing DEL number %u\n",
418                 crc->i);
419     crc->data = NULL;
420     GNUNET_CRYPTO_hash (&crc->i, sizeof(int), &crc->key);
421     GNUNET_assert (NULL !=
422                    GNUNET_DATASTORE_get_key (datastore,
423                                              0,
424                                              false,
425                                              &crc->key,
426                                              get_type (crc->i),
427                                              1,
428                                              1,
429                                              &delete_value,
430                                              crc));
431     break;
432
433   case RP_DO_DEL:
434     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
435                 "Executing DO_DEL number %u\n",
436                 crc->i);
437     if (crc->i == 0)
438     {
439       crc->i = ITERATIONS;
440       crc->phase = RP_DELVALIDATE;
441     }
442     else
443     {
444       crc->phase = RP_DEL;
445     }
446     GNUNET_assert (NULL !=
447                    GNUNET_DATASTORE_remove (datastore, &crc->key, crc->size,
448                                             crc->data, 1, 1,
449                                             &check_success, crc));
450     break;
451
452   case RP_DELVALIDATE:
453     crc->i--;
454     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
455                 "Executing DELVALIDATE number %u\n",
456                 crc->i);
457     GNUNET_CRYPTO_hash (&crc->i, sizeof(int), &crc->key);
458     GNUNET_assert (NULL !=
459                    GNUNET_DATASTORE_get_key (datastore,
460                                              0,
461                                              false,
462                                              &crc->key,
463                                              get_type (crc->i),
464                                              1,
465                                              1,
466                                              &check_nothing,
467                                              crc));
468     break;
469
470   case RP_RESERVE:
471     crc->phase = RP_PUT_MULTIPLE;
472     GNUNET_DATASTORE_reserve (datastore, 128 * 1024, 2,
473                               &get_reserved, crc);
474     break;
475
476   case RP_PUT_MULTIPLE:
477     crc->phase = RP_PUT_MULTIPLE_NEXT;
478     GNUNET_DATASTORE_put (datastore, crc->rid, &crc->key, get_size (42),
479                           get_data (42), get_type (42), get_priority (42),
480                           get_anonymity (42), 0, get_expiration (42), 1, 1,
481                           &check_success, crc);
482     break;
483
484   case RP_PUT_MULTIPLE_NEXT:
485     crc->phase = RP_GET_MULTIPLE;
486     GNUNET_DATASTORE_put (datastore, crc->rid,
487                           &crc->key,
488                           get_size (43),
489                           get_data (43),
490                           get_type (42),
491                           get_priority (43),
492                           get_anonymity (43),
493                           0,
494                           get_expiration (43),
495                           1, 1,
496                           &check_success, crc);
497     break;
498
499   case RP_GET_MULTIPLE:
500     GNUNET_assert (NULL !=
501                    GNUNET_DATASTORE_get_key (datastore,
502                                              0,
503                                              false,
504                                              &crc->key,
505                                              get_type (42),
506                                              1,
507                                              1,
508                                              &check_multiple,
509                                              crc));
510     break;
511
512   case RP_GET_MULTIPLE_NEXT:
513     GNUNET_assert (NULL !=
514                    GNUNET_DATASTORE_get_key (datastore,
515                                              crc->first_uid + 1,
516                                              false,
517                                              &crc->key,
518                                              get_type (42),
519                                              1,
520                                              1,
521                                              &check_multiple,
522                                              crc));
523     break;
524
525   case RP_DONE:
526     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
527                 "Finished, disconnecting\n");
528     GNUNET_DATASTORE_disconnect (datastore,
529                                  GNUNET_YES);
530     GNUNET_free (crc);
531     ok = 0;
532     break;
533
534   case RP_ERROR:
535     GNUNET_DATASTORE_disconnect (datastore,
536                                  GNUNET_YES);
537     GNUNET_free (crc);
538     ok = 43;
539     break;
540   }
541 }
542
543
544 /**
545  * Function called with the result of the initial PUT operation.  If
546  * the PUT succeeded, we start the actual benchmark loop, otherwise we
547  * bail out with an error.
548  *
549  *
550  * @param cls closure
551  * @param success #GNUNET_SYSERR on failure
552  * @param min_expiration minimum expiration time required for content to be stored
553  *                by the datacache at this time, zero for unknown
554  * @param msg NULL on success, otherwise an error message
555  */
556 static void
557 run_tests (void *cls,
558            int32_t success,
559            struct GNUNET_TIME_Absolute min_expiration,
560            const char *msg)
561 {
562   struct CpsRunContext *crc = cls;
563
564   switch (success)
565   {
566   case GNUNET_YES:
567     GNUNET_SCHEDULER_add_now (&run_continuation,
568                               crc);
569     return;
570
571   case GNUNET_NO:
572     fprintf (stderr,
573              "%s", "Test 'put' operation failed, key already exists (!?)\n");
574     GNUNET_DATASTORE_disconnect (datastore,
575                                  GNUNET_YES);
576     GNUNET_free (crc);
577     return;
578
579   case GNUNET_SYSERR:
580     fprintf (stderr,
581              "Test 'put' operation failed with error `%s' database likely not setup, skipping test.\n",
582              msg);
583     GNUNET_DATASTORE_disconnect (datastore,
584                                  GNUNET_YES);
585     GNUNET_free (crc);
586     return;
587
588   default:
589     GNUNET_assert (0);
590   }
591 }
592
593
594 /**
595  * Beginning of the actual execution of the benchmark.
596  * Performs a first test operation (PUT) to verify that
597  * the plugin works at all.
598  *
599  * @param cls NULL
600  * @param cfg configuration to use
601  * @param peer peer handle (unused)
602  */
603 static void
604 run (void *cls,
605      const struct GNUNET_CONFIGURATION_Handle *cfg,
606      struct GNUNET_TESTING_Peer *peer)
607 {
608   struct CpsRunContext *crc;
609   static struct GNUNET_HashCode zkey;
610
611   crc = GNUNET_new (struct CpsRunContext);
612   crc->phase = RP_PUT;
613   now = GNUNET_TIME_absolute_get ();
614   datastore = GNUNET_DATASTORE_connect (cfg);
615   if (NULL ==
616       GNUNET_DATASTORE_put (datastore,
617                             0,
618                             &zkey,
619                             4,
620                             "TEST",
621                             GNUNET_BLOCK_TYPE_TEST,
622                             0, 0, 0,
623                             GNUNET_TIME_relative_to_absolute
624                               (GNUNET_TIME_UNIT_SECONDS),
625                             0, 1,
626                             &run_tests, crc))
627   {
628     fprintf (stderr,
629              "%s",
630              "Test 'put' operation failed.\n");
631     ok = 1;
632     GNUNET_free (crc);
633   }
634 }
635
636
637 /**
638  * Function invoked to notify service of disk utilization
639  * changes.
640  *
641  * @param cls closure
642  * @param delta change in disk utilization,
643  *        0 for "reset to empty"
644  */
645 static void
646 duc_dummy (void *cls,
647            int delta)
648 {
649   /* intentionally empty */
650 }
651
652
653 /**
654  * check if plugin is actually working
655  */
656 static int
657 test_plugin (const char *cfg_name)
658 {
659   char libname[128];
660   struct GNUNET_CONFIGURATION_Handle *cfg;
661   struct GNUNET_DATASTORE_PluginFunctions *api;
662   struct GNUNET_DATASTORE_PluginEnvironment env;
663
664   cfg = GNUNET_CONFIGURATION_create ();
665   if (GNUNET_OK !=
666       GNUNET_CONFIGURATION_load (cfg,
667                                  cfg_name))
668   {
669     GNUNET_CONFIGURATION_destroy (cfg);
670     fprintf (stderr,
671              "Failed to load configuration %s\n",
672              cfg_name);
673     return 1;
674   }
675   memset (&env, 0, sizeof(env));
676   env.cfg = cfg;
677   env.duc = &duc_dummy;
678   GNUNET_snprintf (libname,
679                    sizeof(libname),
680                    "libgnunet_plugin_datastore_%s",
681                    plugin_name);
682   api = GNUNET_PLUGIN_load (libname, &env);
683   if (NULL == api)
684   {
685     GNUNET_CONFIGURATION_destroy (cfg);
686     fprintf (stderr,
687              "Failed to load plugin `%s'\n",
688              libname);
689     return 77;
690   }
691   GNUNET_PLUGIN_unload (libname, api);
692   GNUNET_CONFIGURATION_destroy (cfg);
693   return 0;
694 }
695
696
697 /**
698  * Entry point into the test. Determines which configuration / plugin
699  * we are running with based on the name of the binary and starts
700  * the peer.
701  *
702  * @param argc should be 1
703  * @param argv used to determine plugin / configuration name.
704  * @return 0 on success
705  */
706 int
707 main (int argc,
708       char *argv[])
709 {
710   char cfg_name[PATH_MAX];
711   int ret;
712
713   plugin_name = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
714   GNUNET_snprintf (cfg_name,
715                    sizeof(cfg_name),
716                    "test_datastore_api_data_%s.conf",
717                    plugin_name);
718   ret = test_plugin (cfg_name);
719   if (0 != ret)
720     return ret;
721   /* run actual test */
722   if (0 !=
723       GNUNET_TESTING_peer_run ("test-gnunet-datastore",
724                                cfg_name,
725                                &run,
726                                NULL))
727     return 1;
728   return ok;
729 }
730
731
732 /* end of test_datastore_api.c */