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