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