25836ca663065cb6169fc0eb7b1feab0e37ffbe9
[oweals/gnunet.git] / src / datastore / test_datastore_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2004, 2005, 2006, 2007, 2009 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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, 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
34 #define VERBOSE GNUNET_NO
35
36 #define START_DATASTORE GNUNET_YES
37
38 /**
39  * How long until we give up on transmitting the message?
40  */
41 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
42
43 #define ITERATIONS 256
44
45 static struct GNUNET_DATASTORE_Handle *datastore;
46
47 static struct GNUNET_TIME_Absolute now;
48
49 static int ok;
50
51 /**
52  * Name of plugin under test.
53  */
54 static const char *plugin_name;
55
56 static size_t
57 get_size (int i)
58 {
59   return 8 * i;
60 }
61
62
63 static const void *
64 get_data (int i)
65 {
66   static char buf[60000];
67
68   memset (buf, i, 8 * i);
69   return buf;
70 }
71
72
73 static int
74 get_type (int i)
75 {
76   return i + 1;
77 }
78
79
80 static int
81 get_priority (int i)
82 {
83   return i + 1;
84 }
85
86
87 static int
88 get_anonymity (int i)
89 {
90   return i;
91 }
92
93
94 static struct GNUNET_TIME_Absolute
95 get_expiration (int i)
96 {
97   struct GNUNET_TIME_Absolute av;
98
99   av.abs_value = now.abs_value + 20000000 - i * 1000;
100   return av;
101 }
102
103 enum RunPhase
104 {
105   RP_DONE = 0,
106   RP_PUT = 1,
107   RP_GET = 2,
108   RP_DEL = 3,
109   RP_DO_DEL = 4,
110   RP_DELVALIDATE = 5,
111   RP_RESERVE = 6,
112   RP_PUT_MULTIPLE = 7,
113   RP_PUT_MULTIPLE_NEXT = 8,
114   RP_GET_MULTIPLE = 9,
115   RP_GET_MULTIPLE_NEXT = 10,
116   RP_UPDATE = 11,
117   RP_UPDATE_VALIDATE = 12,
118   RP_ERROR
119 };
120
121
122 struct CpsRunContext
123 {
124   GNUNET_HashCode key;
125   int i;
126   int rid;
127   const struct GNUNET_CONFIGURATION_Handle *cfg;
128   void *data;
129   size_t size;
130   enum RunPhase phase;
131   uint64_t uid;
132   uint64_t offset;
133   uint64_t first_uid;
134 };
135
136
137 static void
138 run_continuation (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
139
140
141 static void
142 check_success (void *cls, int success, struct GNUNET_TIME_Absolute min_expiration, const char *msg)
143 {
144   struct CpsRunContext *crc = cls;
145
146   if (GNUNET_OK != success)
147   {
148     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
149                 "Operation %d/%d not successfull: `%s'\n", crc->phase, crc->i,
150                 msg);
151     crc->phase = RP_ERROR;
152   }
153   GNUNET_free_non_null (crc->data);
154   crc->data = NULL;
155   GNUNET_SCHEDULER_add_continuation (&run_continuation, crc,
156                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
157 }
158
159
160 static void
161 get_reserved (void *cls, int success, struct GNUNET_TIME_Absolute min_expiration, const char *msg)
162 {
163   struct CpsRunContext *crc = cls;
164
165   if (0 >= success)
166     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error obtaining reservation: `%s'\n",
167                 msg);
168   GNUNET_assert (0 < success);
169   crc->rid = success;
170   GNUNET_SCHEDULER_add_continuation (&run_continuation, crc,
171                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
172 }
173
174
175 static void
176 check_value (void *cls, const GNUNET_HashCode * key, size_t size,
177              const void *data, enum GNUNET_BLOCK_Type type, uint32_t priority,
178              uint32_t anonymity, struct GNUNET_TIME_Absolute expiration,
179              uint64_t uid)
180 {
181   struct CpsRunContext *crc = cls;
182   int i;
183
184   i = crc->i;
185   if (NULL == key)
186   {
187     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
188                 "Value check failed (got NULL key) in %d/%d\n", crc->phase,
189                 crc->i);
190     crc->phase = RP_ERROR;
191     GNUNET_SCHEDULER_add_continuation (&run_continuation, crc,
192                                        GNUNET_SCHEDULER_REASON_PREREQ_DONE);
193     return;
194   }
195 #if 0
196   FPRINTF (stderr, "Check value got `%s' of size %u, type %d, expire %llu\n",
197            GNUNET_h2s (key), (unsigned int) size, type,
198            (unsigned long long) expiration.abs_value);
199   FPRINTF (stderr,
200            "Check value iteration %d wants size %u, type %d, expire %llu\n", i,
201            (unsigned int) get_size (i), get_type (i),
202            (unsigned long long) get_expiration (i).abs_value);
203 #endif
204   GNUNET_assert (size == get_size (i));
205   GNUNET_assert (0 == memcmp (data, get_data (i), size));
206   GNUNET_assert (type == get_type (i));
207   GNUNET_assert (priority == get_priority (i));
208   GNUNET_assert (anonymity == get_anonymity (i));
209   GNUNET_assert (expiration.abs_value == get_expiration (i).abs_value);
210   crc->offset++;
211   if (crc->i == 0)
212   {
213     crc->phase = RP_DEL;
214     crc->i = ITERATIONS;
215   }
216   GNUNET_SCHEDULER_add_continuation (&run_continuation, crc,
217                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
218 }
219
220
221 static void
222 delete_value (void *cls, const GNUNET_HashCode * key, size_t size,
223               const void *data, enum GNUNET_BLOCK_Type type, uint32_t priority,
224               uint32_t anonymity, struct GNUNET_TIME_Absolute expiration,
225               uint64_t uid)
226 {
227   struct CpsRunContext *crc = cls;
228
229   GNUNET_assert (crc->data == NULL);
230   GNUNET_assert (NULL != key);
231   crc->size = size;
232   crc->key = *key;
233   crc->data = GNUNET_malloc (size);
234   memcpy (crc->data, data, size);
235   crc->phase = RP_DO_DEL;
236   GNUNET_SCHEDULER_add_continuation (&run_continuation, crc,
237                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
238 }
239
240
241 static void
242 check_nothing (void *cls, const GNUNET_HashCode * key, size_t size,
243                const void *data, enum GNUNET_BLOCK_Type type, uint32_t priority,
244                uint32_t anonymity, struct GNUNET_TIME_Absolute expiration,
245                uint64_t uid)
246 {
247   struct CpsRunContext *crc = cls;
248
249   GNUNET_assert (key == NULL);
250   if (crc->i == 0)
251     crc->phase = RP_RESERVE;
252   GNUNET_SCHEDULER_add_continuation (&run_continuation, crc,
253                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
254 }
255
256
257 static void
258 check_multiple (void *cls, const GNUNET_HashCode * key, size_t size,
259                 const void *data, enum GNUNET_BLOCK_Type type,
260                 uint32_t priority, uint32_t anonymity,
261                 struct GNUNET_TIME_Absolute expiration, uint64_t uid)
262 {
263   struct CpsRunContext *crc = cls;
264
265   GNUNET_assert (key != NULL);
266   switch (crc->phase)
267   {
268   case RP_GET_MULTIPLE:
269     crc->phase = RP_GET_MULTIPLE_NEXT;
270     crc->first_uid = uid;
271     crc->offset++;
272     break;
273   case RP_GET_MULTIPLE_NEXT:
274     GNUNET_assert (uid != crc->first_uid);
275     crc->phase = RP_UPDATE;
276     break;
277   default:
278     GNUNET_break (0);
279     crc->phase = RP_ERROR;
280     break;
281   }
282   if (priority == get_priority (42))
283     crc->uid = uid;
284   GNUNET_SCHEDULER_add_continuation (&run_continuation, crc,
285                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
286 }
287
288
289 static void
290 check_update (void *cls, const GNUNET_HashCode * key, size_t size,
291               const void *data, enum GNUNET_BLOCK_Type type, uint32_t priority,
292               uint32_t anonymity, struct GNUNET_TIME_Absolute expiration,
293               uint64_t uid)
294 {
295   struct CpsRunContext *crc = cls;
296
297   GNUNET_assert (key != NULL);
298   if ((anonymity == get_anonymity (42)) && (size == get_size (42)) &&
299       (priority == get_priority (42) + 100))
300     crc->phase = RP_DONE;
301   else
302   {
303     GNUNET_assert (size == get_size (43));
304     crc->offset++;
305   }
306   GNUNET_SCHEDULER_add_continuation (&run_continuation, crc,
307                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
308 }
309
310
311 static void
312 run_continuation (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
313 {
314   struct CpsRunContext *crc = cls;
315
316   ok = (int) crc->phase;
317 #if VERBOSE
318   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test in phase %u\n", crc->phase);
319 #endif
320   switch (crc->phase)
321   {
322   case RP_PUT:
323 #if VERBOSE
324     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Executing `%s' number %u\n", "PUT",
325                 crc->i);
326 #endif
327     GNUNET_CRYPTO_hash (&crc->i, sizeof (int), &crc->key);
328     GNUNET_DATASTORE_put (datastore, 0, &crc->key, get_size (crc->i),
329                           get_data (crc->i), get_type (crc->i),
330                           get_priority (crc->i), get_anonymity (crc->i), 0,
331                           get_expiration (crc->i), 1, 1, TIMEOUT,
332                           &check_success, crc);
333     crc->i++;
334     if (crc->i == ITERATIONS)
335       crc->phase = RP_GET;
336     break;
337   case RP_GET:
338     crc->i--;
339 #if VERBOSE
340     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Executing `%s' number %u\n", "GET",
341                 crc->i);
342 #endif
343     GNUNET_CRYPTO_hash (&crc->i, sizeof (int), &crc->key);
344     GNUNET_DATASTORE_get_key (datastore, crc->offset, &crc->key,
345                               get_type (crc->i), 1, 1, TIMEOUT, &check_value,
346                               crc);
347     break;
348   case RP_DEL:
349     crc->i--;
350 #if VERBOSE
351     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Executing `%s' number %u\n", "DEL",
352                 crc->i);
353 #endif
354     crc->data = NULL;
355     GNUNET_CRYPTO_hash (&crc->i, sizeof (int), &crc->key);
356     GNUNET_assert (NULL !=
357                    GNUNET_DATASTORE_get_key (datastore, crc->offset, &crc->key,
358                                              get_type (crc->i), 1, 1, TIMEOUT,
359                                              &delete_value, crc));
360     break;
361   case RP_DO_DEL:
362 #if VERBOSE
363     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Executing `%s' number %u\n", "DO_DEL",
364                 crc->i);
365 #endif
366     if (crc->i == 0)
367     {
368       crc->i = ITERATIONS;
369       crc->phase = RP_DELVALIDATE;
370     }
371     else
372     {
373       crc->phase = RP_DEL;
374     }
375     GNUNET_assert (NULL !=
376                    GNUNET_DATASTORE_remove (datastore, &crc->key, crc->size,
377                                             crc->data, 1, 1, TIMEOUT,
378                                             &check_success, crc));
379     break;
380   case RP_DELVALIDATE:
381     crc->i--;
382 #if VERBOSE
383     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Executing `%s' number %u\n",
384                 "DEL-VALIDATE", crc->i);
385 #endif
386     GNUNET_CRYPTO_hash (&crc->i, sizeof (int), &crc->key);
387     GNUNET_assert (NULL !=
388                    GNUNET_DATASTORE_get_key (datastore, crc->offset, &crc->key,
389                                              get_type (crc->i), 1, 1, TIMEOUT,
390                                              &check_nothing, crc));
391     break;
392   case RP_RESERVE:
393     crc->phase = RP_PUT_MULTIPLE;
394     GNUNET_DATASTORE_reserve (datastore, 128 * 1024, 2, 1, 1, TIMEOUT,
395                               &get_reserved, crc);
396     break;
397   case RP_PUT_MULTIPLE:
398     crc->phase = RP_PUT_MULTIPLE_NEXT;
399     GNUNET_DATASTORE_put (datastore, crc->rid, &crc->key, get_size (42),
400                           get_data (42), get_type (42), get_priority (42),
401                           get_anonymity (42), 0, get_expiration (42), 1, 1,
402                           TIMEOUT, &check_success, crc);
403     break;
404   case RP_PUT_MULTIPLE_NEXT:
405     crc->phase = RP_GET_MULTIPLE;
406     GNUNET_DATASTORE_put (datastore, crc->rid, &crc->key, get_size (43),
407                           get_data (43), get_type (42), get_priority (43),
408                           get_anonymity (43), 0, get_expiration (43), 1, 1,
409                           TIMEOUT, &check_success, crc);
410     break;
411   case RP_GET_MULTIPLE:
412     GNUNET_assert (NULL !=
413                    GNUNET_DATASTORE_get_key (datastore, crc->offset, &crc->key,
414                                              get_type (42), 1, 1, TIMEOUT,
415                                              &check_multiple, crc));
416     break;
417   case RP_GET_MULTIPLE_NEXT:
418     GNUNET_assert (NULL !=
419                    GNUNET_DATASTORE_get_key (datastore, crc->offset, &crc->key,
420                                              get_type (42), 1, 1, TIMEOUT,
421                                              &check_multiple, crc));
422     break;
423   case RP_UPDATE:
424     GNUNET_assert (crc->uid > 0);
425     crc->phase = RP_UPDATE_VALIDATE;
426     GNUNET_DATASTORE_update (datastore, crc->uid, 100, get_expiration (42), 1,
427                              1, TIMEOUT, &check_success, crc);
428     break;
429   case RP_UPDATE_VALIDATE:
430     GNUNET_assert (NULL !=
431                    GNUNET_DATASTORE_get_key (datastore, crc->offset, &crc->key,
432                                              get_type (42), 1, 1, TIMEOUT,
433                                              &check_update, crc));
434     break;
435   case RP_DONE:
436 #if VERBOSE
437     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Finished, disconnecting\n");
438 #endif
439     GNUNET_DATASTORE_disconnect (datastore, GNUNET_YES);
440     GNUNET_free (crc);
441     ok = 0;
442     break;
443   case RP_ERROR:
444     GNUNET_DATASTORE_disconnect (datastore, GNUNET_YES);
445     GNUNET_free (crc);
446     ok = 43;
447     break;
448   }
449 }
450
451
452 static void
453 run_tests (void *cls, int32_t success, struct GNUNET_TIME_Absolute min_expiration, const char *msg)
454 {
455   struct CpsRunContext *crc = cls;
456
457   switch (success)
458   {
459   case GNUNET_YES:
460     GNUNET_SCHEDULER_add_continuation (&run_continuation, crc,
461                                        GNUNET_SCHEDULER_REASON_PREREQ_DONE);
462     return;
463   case GNUNET_NO:
464     FPRINTF (stderr, "%s", "Test 'put' operation failed, key already exists (!?)\n");
465     GNUNET_free (crc);
466     return;
467   case GNUNET_SYSERR:
468     FPRINTF (stderr,
469              "Test 'put' operation failed with error `%s' database likely not setup, skipping test.\n",
470              msg);
471     GNUNET_free (crc);
472     return;
473   default:
474     GNUNET_assert (0);
475   }
476 }
477
478
479 static void
480 run (void *cls, char *const *args, const char *cfgfile,
481      const struct GNUNET_CONFIGURATION_Handle *cfg)
482 {
483   struct CpsRunContext *crc;
484   static GNUNET_HashCode zkey;
485
486   crc = GNUNET_malloc (sizeof (struct CpsRunContext));
487   crc->cfg = cfg;
488   crc->phase = RP_PUT;
489   now = GNUNET_TIME_absolute_get ();
490   datastore = GNUNET_DATASTORE_connect (cfg);
491   if (NULL ==
492       GNUNET_DATASTORE_put (datastore, 0, &zkey, 4, "TEST",
493                             GNUNET_BLOCK_TYPE_TEST, 0, 0, 0,
494                             GNUNET_TIME_relative_to_absolute
495                             (GNUNET_TIME_UNIT_SECONDS), 0, 1,
496                             GNUNET_TIME_UNIT_MINUTES, &run_tests, crc))
497   {
498     FPRINTF (stderr, "%s",  "Test 'put' operation failed.\n");
499     ok = 1;
500     GNUNET_free (crc);
501   }
502 }
503
504
505 static int
506 check ()
507 {
508   char cfg_name[128];
509
510 #if START_DATASTORE
511   struct GNUNET_OS_Process *proc;
512 #endif
513   char *const argv[] = {
514     "test-datastore-api",
515     "-c",
516     cfg_name,
517 #if VERBOSE
518     "-L", "DEBUG",
519 #endif
520     NULL
521   };
522   struct GNUNET_GETOPT_CommandLineOption options[] = {
523     GNUNET_GETOPT_OPTION_END
524   };
525   GNUNET_snprintf (cfg_name, sizeof (cfg_name),
526                    "test_datastore_api_data_%s.conf", plugin_name);
527 #if START_DATASTORE
528   proc =
529     GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-service-arm",
530                                "gnunet-service-arm",
531 #if VERBOSE
532                                "-L", "DEBUG",
533 #endif
534                                "-c", cfg_name, NULL);
535 #endif
536   GNUNET_assert (NULL != proc);
537   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv,
538                       "test-datastore-api", "nohelp", options, &run, NULL);
539 #if START_DATASTORE
540   sleep (1);                    /* give datastore chance to receive 'DROP' request */
541   if (0 != GNUNET_OS_process_kill (proc, SIGTERM))
542   {
543     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
544     ok = 1;
545   }
546   GNUNET_OS_process_wait (proc);
547   GNUNET_OS_process_close (proc);
548   proc = NULL;
549 #endif
550   if (ok != 0)
551     FPRINTF (stderr, "Missed some testcases: %u\n", ok);
552   return ok;
553 }
554
555 int
556 main (int argc, char *argv[])
557 {
558   int ret;
559   char *pos;
560   char dir_name[128];
561
562   sleep (1);
563   /* determine name of plugin to use */
564   plugin_name = argv[0];
565   while (NULL != (pos = strstr (plugin_name, "_")))
566     plugin_name = pos + 1;
567   if (NULL != (pos = strstr (plugin_name, ".")))
568     pos[0] = 0;
569   else
570     pos = (char *) plugin_name;
571
572   GNUNET_snprintf (dir_name, sizeof (dir_name), "/tmp/test-gnunet-datastore-%s",
573                    plugin_name);
574   GNUNET_DISK_directory_remove (dir_name);
575   GNUNET_log_setup ("test-datastore-api",
576 #if VERBOSE
577                     "DEBUG",
578 #else
579                     "WARNING",
580 #endif
581                     NULL);
582   ret = check ();
583   if (pos != plugin_name)
584     pos[0] = '.';
585   GNUNET_DISK_directory_remove (dir_name);
586   return ret;
587 }
588
589 /* end of test_datastore_api.c */