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