-preparations for replacement of try_connect call
[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 Christian Grothoff (and other contributing authors)
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  * @param tc scheduler context (unused)
172  */
173 static void
174 run_continuation (void *cls,
175                   const struct GNUNET_SCHEDULER_TaskContext *tc);
176
177
178 /**
179  * Continuation called to notify client about result of an
180  * operation.  Checks for errors, updates our iteration counters and
181  * continues execution with #run_continuation().
182  *
183  * @param cls the `struct CpsRunContext`
184  * @param success #GNUNET_SYSERR on failure
185  * @param min_expiration minimum expiration time required for content to be stored
186  *                by the datacache at this time, zero for unknown
187  * @param msg NULL on success, otherwise an error message
188  */
189 static void
190 check_success (void *cls,
191                int success,
192                struct GNUNET_TIME_Absolute min_expiration,
193                const char *msg)
194 {
195   struct CpsRunContext *crc = cls;
196
197   if (GNUNET_OK != success)
198   {
199     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
200                 "Operation %d/%d not successfull: `%s'\n",
201                 crc->phase,
202                 crc->i,
203                 msg);
204     crc->phase = RP_ERROR;
205   }
206   GNUNET_free_non_null (crc->data);
207   crc->data = NULL;
208   GNUNET_SCHEDULER_add_now (&run_continuation, crc);
209 }
210
211
212 static void
213 get_reserved (void *cls,
214               int success,
215               struct GNUNET_TIME_Absolute min_expiration,
216               const char *msg)
217 {
218   struct CpsRunContext *crc = cls;
219
220   if (0 >= success)
221     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
222                 "Error obtaining reservation: `%s'\n",
223                 msg);
224   GNUNET_assert (0 < success);
225   crc->rid = success;
226   GNUNET_SCHEDULER_add_now (&run_continuation,
227                             crc);
228 }
229
230
231 static void
232 check_value (void *cls,
233              const struct GNUNET_HashCode *key,
234              size_t size,
235              const void *data,
236              enum GNUNET_BLOCK_Type type,
237              uint32_t priority,
238              uint32_t anonymity,
239              struct GNUNET_TIME_Absolute expiration,
240              uint64_t uid)
241 {
242   struct CpsRunContext *crc = cls;
243   int i;
244
245   i = crc->i;
246   if (NULL == key)
247   {
248     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
249                 "Value check failed (got NULL key) in %d/%d\n",
250                 crc->phase,
251                 crc->i);
252     crc->phase = RP_ERROR;
253     GNUNET_SCHEDULER_add_now (&run_continuation,
254                               crc);
255     return;
256   }
257 #if 0
258   FPRINTF (stderr,
259            "Check value got `%s' of size %u, type %d, expire %s\n",
260            GNUNET_h2s (key), (unsigned int) size, type,
261            GNUNET_STRINGS_absolute_time_to_string (expiration));
262   FPRINTF (stderr,
263            "Check value iteration %d wants size %u, type %d, expire %s\n", i,
264            (unsigned int) get_size (i), get_type (i),
265            GNUNET_STRINGS_absolute_time_to_string (get_expiration(i)));
266 #endif
267   GNUNET_assert (size == get_size (i));
268   GNUNET_assert (0 == memcmp (data, get_data (i), size));
269   GNUNET_assert (type == get_type (i));
270   GNUNET_assert (priority == get_priority (i));
271   GNUNET_assert (anonymity == get_anonymity (i));
272   GNUNET_assert (expiration.abs_value_us == get_expiration (i).abs_value_us);
273   crc->offset++;
274   if (crc->i == 0)
275   {
276     crc->phase = RP_DEL;
277     crc->i = ITERATIONS;
278   }
279   GNUNET_SCHEDULER_add_now (&run_continuation,
280                             crc);
281 }
282
283
284 static void
285 delete_value (void *cls,
286               const struct GNUNET_HashCode *key,
287               size_t size,
288               const void *data,
289               enum GNUNET_BLOCK_Type type,
290               uint32_t priority,
291               uint32_t anonymity,
292               struct GNUNET_TIME_Absolute expiration,
293               uint64_t uid)
294 {
295   struct CpsRunContext *crc = cls;
296
297   GNUNET_assert (NULL == crc->data);
298   GNUNET_assert (NULL != key);
299   crc->size = size;
300   crc->key = *key;
301   crc->data = GNUNET_malloc (size);
302   memcpy (crc->data, data, size);
303   crc->phase = RP_DO_DEL;
304   GNUNET_SCHEDULER_add_now (&run_continuation,
305                             crc);
306 }
307
308
309 static void
310 check_nothing (void *cls,
311                const struct GNUNET_HashCode *key,
312                size_t size,
313                const void *data,
314                enum GNUNET_BLOCK_Type type,
315                uint32_t priority,
316                uint32_t anonymity,
317                struct GNUNET_TIME_Absolute expiration,
318                uint64_t uid)
319 {
320   struct CpsRunContext *crc = cls;
321
322   GNUNET_assert (key == NULL);
323   if (crc->i == 0)
324     crc->phase = RP_RESERVE;
325   GNUNET_SCHEDULER_add_now (&run_continuation,
326                             crc);
327 }
328
329
330 static void
331 check_multiple (void *cls,
332                 const struct GNUNET_HashCode *key,
333                 size_t size,
334                 const void *data,
335                 enum GNUNET_BLOCK_Type type,
336                 uint32_t priority,
337                 uint32_t anonymity,
338                 struct GNUNET_TIME_Absolute expiration,
339                 uint64_t uid)
340 {
341   struct CpsRunContext *crc = cls;
342
343   GNUNET_assert (key != NULL);
344   switch (crc->phase)
345   {
346   case RP_GET_MULTIPLE:
347     crc->phase = RP_GET_MULTIPLE_NEXT;
348     crc->first_uid = uid;
349     crc->offset++;
350     break;
351   case RP_GET_MULTIPLE_NEXT:
352     GNUNET_assert (uid != crc->first_uid);
353     crc->phase = RP_UPDATE;
354     break;
355   default:
356     GNUNET_break (0);
357     crc->phase = RP_ERROR;
358     break;
359   }
360   if (priority == get_priority (42))
361     crc->uid = uid;
362   GNUNET_SCHEDULER_add_now (&run_continuation, crc);
363 }
364
365
366 static void
367 check_update (void *cls,
368               const struct GNUNET_HashCode *key,
369               size_t size,
370               const void *data,
371               enum GNUNET_BLOCK_Type type,
372               uint32_t priority,
373               uint32_t anonymity,
374               struct GNUNET_TIME_Absolute expiration,
375               uint64_t uid)
376 {
377   struct CpsRunContext *crc = cls;
378
379   GNUNET_assert (key != NULL);
380   if ((anonymity == get_anonymity (42)) && (size == get_size (42)) &&
381       (priority == get_priority (42) + 100))
382     crc->phase = RP_DONE;
383   else
384   {
385     GNUNET_assert (size == get_size (43));
386     crc->offset++;
387   }
388   GNUNET_SCHEDULER_add_now (&run_continuation, crc);
389 }
390
391
392 /**
393  * Main state machine.  Executes the next step of the test
394  * depending on the current state.
395  *
396  * @param cls the `struct CpsRunContext`
397  * @param tc scheduler context (unused)
398  */
399 static void
400 run_continuation (void *cls,
401                   const struct GNUNET_SCHEDULER_TaskContext *tc)
402 {
403   struct CpsRunContext *crc = cls;
404
405   ok = (int) crc->phase;
406   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
407               "Test in phase %u\n",
408               crc->phase);
409   switch (crc->phase)
410   {
411   case RP_PUT:
412     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
413                 "Executing `%s' number %u\n",
414                 "PUT",
415                 crc->i);
416     GNUNET_CRYPTO_hash (&crc->i, sizeof (int), &crc->key);
417     GNUNET_DATASTORE_put (datastore, 0, &crc->key, get_size (crc->i),
418                           get_data (crc->i), get_type (crc->i),
419                           get_priority (crc->i), get_anonymity (crc->i), 0,
420                           get_expiration (crc->i), 1, 1, TIMEOUT,
421                           &check_success, crc);
422     crc->i++;
423     if (crc->i == ITERATIONS)
424       crc->phase = RP_GET;
425     break;
426   case RP_GET:
427     crc->i--;
428     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
429                 "Executing `%s' number %u\n",
430                 "GET",
431                 crc->i);
432     GNUNET_CRYPTO_hash (&crc->i, sizeof (int), &crc->key);
433     GNUNET_DATASTORE_get_key (datastore, crc->offset, &crc->key,
434                               get_type (crc->i), 1, 1, TIMEOUT, &check_value,
435                               crc);
436     break;
437   case RP_DEL:
438     crc->i--;
439     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
440                 "Executing `%s' number %u\n",
441                 "DEL",
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, crc->offset, &crc->key,
447                                              get_type (crc->i), 1, 1, TIMEOUT,
448                                              &delete_value, crc));
449     break;
450   case RP_DO_DEL:
451     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
452                 "Executing `%s' number %u\n",
453                 "DO_DEL",
454                 crc->i);
455     if (crc->i == 0)
456     {
457       crc->i = ITERATIONS;
458       crc->phase = RP_DELVALIDATE;
459     }
460     else
461     {
462       crc->phase = RP_DEL;
463     }
464     GNUNET_assert (NULL !=
465                    GNUNET_DATASTORE_remove (datastore, &crc->key, crc->size,
466                                             crc->data, 1, 1, TIMEOUT,
467                                             &check_success, crc));
468     break;
469   case RP_DELVALIDATE:
470     crc->i--;
471     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
472                 "Executing `%s' number %u\n",
473                 "DEL-VALIDATE", crc->i);
474     GNUNET_CRYPTO_hash (&crc->i, sizeof (int), &crc->key);
475     GNUNET_assert (NULL !=
476                    GNUNET_DATASTORE_get_key (datastore, crc->offset, &crc->key,
477                                              get_type (crc->i), 1, 1, TIMEOUT,
478                                              &check_nothing, crc));
479     break;
480   case RP_RESERVE:
481     crc->phase = RP_PUT_MULTIPLE;
482     GNUNET_DATASTORE_reserve (datastore, 128 * 1024, 2,
483                               &get_reserved, crc);
484     break;
485   case RP_PUT_MULTIPLE:
486     crc->phase = RP_PUT_MULTIPLE_NEXT;
487     GNUNET_DATASTORE_put (datastore, crc->rid, &crc->key, get_size (42),
488                           get_data (42), get_type (42), get_priority (42),
489                           get_anonymity (42), 0, get_expiration (42), 1, 1,
490                           TIMEOUT, &check_success, crc);
491     break;
492   case RP_PUT_MULTIPLE_NEXT:
493     crc->phase = RP_GET_MULTIPLE;
494     GNUNET_DATASTORE_put (datastore, crc->rid,
495                           &crc->key,
496                           get_size (43),
497                           get_data (43),
498                           get_type (42),
499                           get_priority (43),
500                           get_anonymity (43),
501                           0,
502                           get_expiration (43),
503                           1, 1,
504                           TIMEOUT,
505                           &check_success, crc);
506     break;
507   case RP_GET_MULTIPLE:
508     GNUNET_assert (NULL !=
509                    GNUNET_DATASTORE_get_key (datastore,
510                                              crc->offset,
511                                              &crc->key,
512                                              get_type (42), 1, 1,
513                                              TIMEOUT,
514                                              &check_multiple, crc));
515     break;
516   case RP_GET_MULTIPLE_NEXT:
517     GNUNET_assert (NULL !=
518                    GNUNET_DATASTORE_get_key (datastore,
519                                              crc->offset,
520                                              &crc->key,
521                                              get_type (42),
522                                              1, 1,
523                                              TIMEOUT,
524                                              &check_multiple, crc));
525     break;
526   case RP_UPDATE:
527     GNUNET_assert (crc->uid > 0);
528     crc->phase = RP_UPDATE_VALIDATE;
529     GNUNET_DATASTORE_update (datastore,
530                              crc->uid, 100,
531                              get_expiration (42), 1,
532                              1, TIMEOUT,
533                              &check_success, crc);
534     break;
535   case RP_UPDATE_VALIDATE:
536     GNUNET_assert (NULL !=
537                    GNUNET_DATASTORE_get_key (datastore,
538                                              crc->offset,
539                                              &crc->key,
540                                              get_type (42),
541                                              1, 1,
542                                              TIMEOUT,
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                             TIMEOUT,
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  * Entry point into the test. Determines which configuration / plugin
656  * we are running with based on the name of the binary and starts
657  * the peer.
658  *
659  * @param argc should be 1
660  * @param argv used to determine plugin / configuration name.
661  * @return 0 on success
662  */
663 int
664 main (int argc,
665       char *argv[])
666 {
667   char cfg_name[128];
668
669   plugin_name = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
670   GNUNET_snprintf (cfg_name,
671                    sizeof (cfg_name),
672                    "test_datastore_api_data_%s.conf",
673                    plugin_name);
674   if (0 !=
675       GNUNET_TESTING_peer_run ("test-gnunet-datastore",
676                                cfg_name,
677                                &run,
678                                NULL))
679     return 1;
680   return ok;
681 }
682
683 /* end of test_datastore_api.c */