a996682408045cd26c809f30bb296a7ca2c90bbb
[oweals/gnunet.git] / 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
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
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 uid;
160   uint64_t offset;
161   uint64_t first_uid;
162 };
163
164
165 /**
166  * Main state machine.  Executes the next step of the test
167  * depending on the current state.
168  *
169  * @param cls the `struct CpsRunContext`
170  */
171 static void
172 run_continuation (void *cls);
173
174
175 /**
176  * Continuation called to notify client about result of an
177  * operation.  Checks for errors, updates our iteration counters and
178  * continues execution with #run_continuation().
179  *
180  * @param cls the `struct CpsRunContext`
181  * @param success #GNUNET_SYSERR on failure
182  * @param min_expiration minimum expiration time required for content to be stored
183  *                by the datacache at this time, zero for unknown
184  * @param msg NULL on success, otherwise an error message
185  */
186 static void
187 check_success (void *cls,
188                int success,
189                struct GNUNET_TIME_Absolute min_expiration,
190                const char *msg)
191 {
192   struct CpsRunContext *crc = cls;
193
194   if (GNUNET_OK != success)
195   {
196     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
197                 "Operation %d/%d not successful: `%s'\n",
198                 crc->phase,
199                 crc->i,
200                 msg);
201     crc->phase = RP_ERROR;
202   }
203   GNUNET_free_non_null (crc->data);
204   crc->data = NULL;
205   GNUNET_SCHEDULER_add_now (&run_continuation, crc);
206 }
207
208
209 static void
210 get_reserved (void *cls,
211               int success,
212               struct GNUNET_TIME_Absolute min_expiration,
213               const char *msg)
214 {
215   struct CpsRunContext *crc = cls;
216
217   if (0 >= success)
218     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
219                 "Error obtaining reservation: `%s'\n",
220                 msg);
221   GNUNET_assert (0 < success);
222   crc->rid = success;
223   GNUNET_SCHEDULER_add_now (&run_continuation,
224                             crc);
225 }
226
227
228 static void
229 check_value (void *cls,
230              const struct GNUNET_HashCode *key,
231              size_t size,
232              const void *data,
233              enum GNUNET_BLOCK_Type type,
234              uint32_t priority,
235              uint32_t anonymity,
236              struct GNUNET_TIME_Absolute expiration,
237              uint64_t uid)
238 {
239   struct CpsRunContext *crc = cls;
240   int i;
241
242   i = crc->i;
243   if (NULL == key)
244   {
245     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
246                 "Value check failed (got NULL key) in %d/%d\n",
247                 crc->phase,
248                 crc->i);
249     crc->phase = RP_ERROR;
250     GNUNET_SCHEDULER_add_now (&run_continuation,
251                               crc);
252     return;
253   }
254 #if 0
255   FPRINTF (stderr,
256            "Check value got `%s' of size %u, type %d, expire %s\n",
257            GNUNET_h2s (key), (unsigned int) size, type,
258            GNUNET_STRINGS_absolute_time_to_string (expiration));
259   FPRINTF (stderr,
260            "Check value iteration %d wants size %u, type %d, expire %s\n", i,
261            (unsigned int) get_size (i), get_type (i),
262            GNUNET_STRINGS_absolute_time_to_string (get_expiration(i)));
263 #endif
264   GNUNET_assert (size == get_size (i));
265   GNUNET_assert (0 == memcmp (data, get_data (i), size));
266   GNUNET_assert (type == get_type (i));
267   GNUNET_assert (priority == get_priority (i));
268   GNUNET_assert (anonymity == get_anonymity (i));
269   GNUNET_assert (expiration.abs_value_us == get_expiration (i).abs_value_us);
270   crc->offset++;
271   if (crc->i == 0)
272   {
273     crc->phase = RP_DEL;
274     crc->i = ITERATIONS;
275   }
276   GNUNET_SCHEDULER_add_now (&run_continuation,
277                             crc);
278 }
279
280
281 static void
282 delete_value (void *cls,
283               const struct GNUNET_HashCode *key,
284               size_t size,
285               const void *data,
286               enum GNUNET_BLOCK_Type type,
287               uint32_t priority,
288               uint32_t anonymity,
289               struct GNUNET_TIME_Absolute expiration,
290               uint64_t uid)
291 {
292   struct CpsRunContext *crc = cls;
293
294   GNUNET_assert (NULL == crc->data);
295   GNUNET_assert (NULL != key);
296   crc->size = size;
297   crc->key = *key;
298   crc->data = GNUNET_malloc (size);
299   GNUNET_memcpy (crc->data, data, size);
300   crc->phase = RP_DO_DEL;
301   GNUNET_SCHEDULER_add_now (&run_continuation,
302                             crc);
303 }
304
305
306 static void
307 check_nothing (void *cls,
308                const struct GNUNET_HashCode *key,
309                size_t size,
310                const void *data,
311                enum GNUNET_BLOCK_Type type,
312                uint32_t priority,
313                uint32_t anonymity,
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                 struct GNUNET_TIME_Absolute expiration,
336                 uint64_t uid)
337 {
338   struct CpsRunContext *crc = cls;
339
340   GNUNET_assert (key != NULL);
341   switch (crc->phase)
342   {
343   case RP_GET_MULTIPLE:
344     crc->phase = RP_GET_MULTIPLE_NEXT;
345     crc->first_uid = uid;
346     crc->offset++;
347     break;
348   case RP_GET_MULTIPLE_NEXT:
349     GNUNET_assert (uid != crc->first_uid);
350     crc->phase = RP_DONE;
351     break;
352   default:
353     GNUNET_break (0);
354     crc->phase = RP_ERROR;
355     break;
356   }
357   if (priority == get_priority (42))
358     crc->uid = uid;
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   case RP_GET:
395     crc->i--;
396     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
397                 "Executing GET number %u\n",
398                 crc->i);
399     GNUNET_CRYPTO_hash (&crc->i,
400                         sizeof (int),
401                         &crc->key);
402     GNUNET_DATASTORE_get_key (datastore,
403                               crc->offset,
404                               &crc->key,
405                               get_type (crc->i),
406                               1,
407                               1,
408                               &check_value,
409                               crc);
410     break;
411   case RP_DEL:
412     crc->i--;
413     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
414                 "Executing DEL number %u\n",
415                 crc->i);
416     crc->data = NULL;
417     GNUNET_CRYPTO_hash (&crc->i, sizeof (int), &crc->key);
418     GNUNET_assert (NULL !=
419                    GNUNET_DATASTORE_get_key (datastore,
420                                              crc->offset,
421                                              &crc->key,
422                                              get_type (crc->i),
423                                              1,
424                                              1,
425                                              &delete_value,
426                                              crc));
427     break;
428   case RP_DO_DEL:
429     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
430                 "Executing DO_DEL number %u\n",
431                 crc->i);
432     if (crc->i == 0)
433     {
434       crc->i = ITERATIONS;
435       crc->phase = RP_DELVALIDATE;
436     }
437     else
438     {
439       crc->phase = RP_DEL;
440     }
441     GNUNET_assert (NULL !=
442                    GNUNET_DATASTORE_remove (datastore, &crc->key, crc->size,
443                                             crc->data, 1, 1,
444                                             &check_success, crc));
445     break;
446   case RP_DELVALIDATE:
447     crc->i--;
448     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
449                 "Executing DELVALIDATE number %u\n",
450                 crc->i);
451     GNUNET_CRYPTO_hash (&crc->i, sizeof (int), &crc->key);
452     GNUNET_assert (NULL !=
453                    GNUNET_DATASTORE_get_key (datastore, crc->offset, &crc->key,
454                                              get_type (crc->i), 1, 1,
455                                              &check_nothing, crc));
456     break;
457   case RP_RESERVE:
458     crc->phase = RP_PUT_MULTIPLE;
459     GNUNET_DATASTORE_reserve (datastore, 128 * 1024, 2,
460                               &get_reserved, crc);
461     break;
462   case RP_PUT_MULTIPLE:
463     crc->phase = RP_PUT_MULTIPLE_NEXT;
464     GNUNET_DATASTORE_put (datastore, crc->rid, &crc->key, get_size (42),
465                           get_data (42), get_type (42), get_priority (42),
466                           get_anonymity (42), 0, get_expiration (42), 1, 1,
467                           &check_success, crc);
468     break;
469   case RP_PUT_MULTIPLE_NEXT:
470     crc->phase = RP_GET_MULTIPLE;
471     GNUNET_DATASTORE_put (datastore, crc->rid,
472                           &crc->key,
473                           get_size (43),
474                           get_data (43),
475                           get_type (42),
476                           get_priority (43),
477                           get_anonymity (43),
478                           0,
479                           get_expiration (43),
480                           1, 1,
481                           &check_success, crc);
482     break;
483   case RP_GET_MULTIPLE:
484     GNUNET_assert (NULL !=
485                    GNUNET_DATASTORE_get_key (datastore,
486                                              crc->offset,
487                                              &crc->key,
488                                              get_type (42), 1, 1,
489                                              &check_multiple, crc));
490     break;
491   case RP_GET_MULTIPLE_NEXT:
492     GNUNET_assert (NULL !=
493                    GNUNET_DATASTORE_get_key (datastore,
494                                              crc->offset,
495                                              &crc->key,
496                                              get_type (42),
497                                              1, 1,
498                                              &check_multiple, crc));
499     break;
500   case RP_DONE:
501     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
502                 "Finished, disconnecting\n");
503     GNUNET_DATASTORE_disconnect (datastore,
504                                  GNUNET_YES);
505     GNUNET_free (crc);
506     ok = 0;
507     break;
508   case RP_ERROR:
509     GNUNET_DATASTORE_disconnect (datastore,
510                                  GNUNET_YES);
511     GNUNET_free (crc);
512     ok = 43;
513     break;
514   }
515 }
516
517
518 /**
519  * Function called with the result of the initial PUT operation.  If
520  * the PUT succeeded, we start the actual benchmark loop, otherwise we
521  * bail out with an error.
522  *
523  *
524  * @param cls closure
525  * @param success #GNUNET_SYSERR on failure
526  * @param min_expiration minimum expiration time required for content to be stored
527  *                by the datacache at this time, zero for unknown
528  * @param msg NULL on success, otherwise an error message
529  */
530 static void
531 run_tests (void *cls,
532            int32_t success,
533            struct GNUNET_TIME_Absolute min_expiration,
534            const char *msg)
535 {
536   struct CpsRunContext *crc = cls;
537
538   switch (success)
539   {
540   case GNUNET_YES:
541     GNUNET_SCHEDULER_add_now (&run_continuation,
542                               crc);
543     return;
544   case GNUNET_NO:
545     FPRINTF (stderr,
546              "%s", "Test 'put' operation failed, key already exists (!?)\n");
547     GNUNET_DATASTORE_disconnect (datastore,
548                                  GNUNET_YES);
549     GNUNET_free (crc);
550     return;
551   case GNUNET_SYSERR:
552     FPRINTF (stderr,
553              "Test 'put' operation failed with error `%s' database likely not setup, skipping test.\n",
554              msg);
555     GNUNET_DATASTORE_disconnect (datastore,
556                                  GNUNET_YES);
557     GNUNET_free (crc);
558     return;
559   default:
560     GNUNET_assert (0);
561   }
562 }
563
564
565 /**
566  * Beginning of the actual execution of the benchmark.
567  * Performs a first test operation (PUT) to verify that
568  * the plugin works at all.
569  *
570  * @param cls NULL
571  * @param cfg configuration to use
572  * @param peer peer handle (unused)
573  */
574 static void
575 run (void *cls,
576      const struct GNUNET_CONFIGURATION_Handle *cfg,
577      struct GNUNET_TESTING_Peer *peer)
578 {
579   struct CpsRunContext *crc;
580   static struct GNUNET_HashCode zkey;
581
582   crc = GNUNET_new (struct CpsRunContext);
583   crc->phase = RP_PUT;
584   now = GNUNET_TIME_absolute_get ();
585   datastore = GNUNET_DATASTORE_connect (cfg);
586   if (NULL ==
587       GNUNET_DATASTORE_put (datastore,
588                             0,
589                             &zkey,
590                             4,
591                             "TEST",
592                             GNUNET_BLOCK_TYPE_TEST,
593                             0, 0, 0,
594                             GNUNET_TIME_relative_to_absolute
595                             (GNUNET_TIME_UNIT_SECONDS),
596                             0, 1,
597                             &run_tests, crc))
598   {
599     FPRINTF (stderr,
600              "%s",
601              "Test 'put' operation failed.\n");
602     ok = 1;
603     GNUNET_free (crc);
604   }
605 }
606
607
608 /**
609  * Function invoked to notify service of disk utilization
610  * changes.
611  *
612  * @param cls closure
613  * @param delta change in disk utilization,
614  *        0 for "reset to empty"
615  */
616 static void
617 duc_dummy (void *cls,
618            int delta)
619 {
620   /* intentionally empty */
621 }
622
623
624 /**
625  * check if plugin is actually working 
626  */
627 static int
628 test_plugin (const char *cfg_name)
629 {
630   char libname[128];
631   struct GNUNET_CONFIGURATION_Handle *cfg;
632   struct GNUNET_DATASTORE_PluginFunctions *api;
633   struct GNUNET_DATASTORE_PluginEnvironment env;
634   
635   cfg = GNUNET_CONFIGURATION_create ();
636   if (GNUNET_OK !=
637       GNUNET_CONFIGURATION_load (cfg,
638                                  cfg_name))
639   {
640     GNUNET_CONFIGURATION_destroy (cfg);
641     fprintf (stderr,
642              "Failed to load configuration %s\n",
643              cfg_name);
644     return 1;
645   }
646   memset (&env, 0, sizeof (env));
647   env.cfg = cfg;
648   env.duc = &duc_dummy;
649   GNUNET_snprintf (libname,
650                    sizeof (libname),
651                    "libgnunet_plugin_datastore_%s",
652                    plugin_name);
653   api = GNUNET_PLUGIN_load (libname, &env);
654   if (NULL == api)
655   {
656     GNUNET_CONFIGURATION_destroy (cfg);
657     fprintf (stderr,
658              "Failed to load plugin `%s'\n",
659              libname);
660     return 77;
661   }
662   GNUNET_PLUGIN_unload (libname, api);
663   GNUNET_CONFIGURATION_destroy (cfg);
664   return 0;
665 }
666
667
668 /**
669  * Entry point into the test. Determines which configuration / plugin
670  * we are running with based on the name of the binary and starts
671  * the peer.
672  *
673  * @param argc should be 1
674  * @param argv used to determine plugin / configuration name.
675  * @return 0 on success
676  */
677 int
678 main (int argc,
679       char *argv[])
680 {
681   char cfg_name[128];
682   int ret;
683     
684   plugin_name = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
685   GNUNET_snprintf (cfg_name,
686                    sizeof (cfg_name),
687                    "test_datastore_api_data_%s.conf",
688                    plugin_name);
689   ret = test_plugin (cfg_name);
690   if (0 != ret)
691     return ret;
692   /* run actual test */
693   if (0 !=
694       GNUNET_TESTING_peer_run ("test-gnunet-datastore",
695                                cfg_name,
696                                &run,
697                                NULL))
698     return 1;
699   return ok;
700 }
701
702 /* end of test_datastore_api.c */