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