39b7a1ae23e43a7b5290c8a8c43ed7106cf70acd
[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, 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",
150                 crc->phase, crc->i, 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,
156                                      crc, GNUNET_SCHEDULER_REASON_PREREQ_DONE);
157 }
158
159
160 static void
161 get_reserved (void *cls, int success, const char *msg)
162 {
163   struct CpsRunContext *crc = cls;
164
165   if (0 >= success)
166     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
167                 "Error obtaining reservation: `%s'\n", msg);
168   GNUNET_assert (0 < success);
169   crc->rid = success;
170   GNUNET_SCHEDULER_add_continuation (&run_continuation,
171                                      crc, GNUNET_SCHEDULER_REASON_PREREQ_DONE);
172 }
173
174
175 static void
176 check_value (void *cls,
177              const GNUNET_HashCode * key,
178              size_t size,
179              const void *data,
180              enum GNUNET_BLOCK_Type type,
181              uint32_t priority,
182              uint32_t anonymity,
183              struct GNUNET_TIME_Absolute expiration, uint64_t uid)
184 {
185   struct CpsRunContext *crc = cls;
186   int i;
187
188   i = crc->i;
189 #if 0
190   fprintf (stderr,
191            "Check value got `%s' of size %u, type %d, expire %llu\n",
192            GNUNET_h2s (key),
193            (unsigned int) size,
194            type, (unsigned long long) expiration.abs_value);
195   fprintf (stderr,
196            "Check value iteration %d wants size %u, type %d, expire %llu\n",
197            i,
198            (unsigned int) get_size (i),
199            get_type (i), (unsigned long long) get_expiration (i).abs_value);
200 #endif
201   GNUNET_assert (size == get_size (i));
202   GNUNET_assert (0 == memcmp (data, get_data (i), size));
203   GNUNET_assert (type == get_type (i));
204   GNUNET_assert (priority == get_priority (i));
205   GNUNET_assert (anonymity == get_anonymity (i));
206   GNUNET_assert (expiration.abs_value == get_expiration (i).abs_value);
207   crc->offset++;
208   if (crc->i == 0)
209   {
210     crc->phase = RP_DEL;
211     crc->i = ITERATIONS;
212   }
213   GNUNET_SCHEDULER_add_continuation (&run_continuation,
214                                      crc, GNUNET_SCHEDULER_REASON_PREREQ_DONE);
215 }
216
217
218 static void
219 delete_value (void *cls,
220               const GNUNET_HashCode * key,
221               size_t size,
222               const void *data,
223               enum GNUNET_BLOCK_Type type,
224               uint32_t priority,
225               uint32_t anonymity,
226               struct GNUNET_TIME_Absolute expiration, uint64_t uid)
227 {
228   struct CpsRunContext *crc = cls;
229
230   GNUNET_assert (crc->data == NULL);
231   GNUNET_assert (NULL != key);
232   crc->size = size;
233   crc->key = *key;
234   crc->data = GNUNET_malloc (size);
235   memcpy (crc->data, data, size);
236   crc->phase = RP_DO_DEL;
237   GNUNET_SCHEDULER_add_continuation (&run_continuation,
238                                      crc, GNUNET_SCHEDULER_REASON_PREREQ_DONE);
239 }
240
241
242 static void
243 check_nothing (void *cls,
244                const GNUNET_HashCode * key,
245                size_t size,
246                const void *data,
247                enum GNUNET_BLOCK_Type type,
248                uint32_t priority,
249                uint32_t anonymity,
250                struct GNUNET_TIME_Absolute expiration, uint64_t uid)
251 {
252   struct CpsRunContext *crc = cls;
253
254   GNUNET_assert (key == NULL);
255   if (crc->i == 0)
256     crc->phase = RP_RESERVE;
257   GNUNET_SCHEDULER_add_continuation (&run_continuation,
258                                      crc, GNUNET_SCHEDULER_REASON_PREREQ_DONE);
259 }
260
261
262 static void
263 check_multiple (void *cls,
264                 const GNUNET_HashCode * key,
265                 size_t size,
266                 const void *data,
267                 enum GNUNET_BLOCK_Type type,
268                 uint32_t priority,
269                 uint32_t anonymity,
270                 struct GNUNET_TIME_Absolute expiration, uint64_t uid)
271 {
272   struct CpsRunContext *crc = cls;
273
274   GNUNET_assert (key != NULL);
275   switch (crc->phase)
276   {
277   case RP_GET_MULTIPLE:
278     crc->phase = RP_GET_MULTIPLE_NEXT;
279     crc->first_uid = uid;
280     crc->offset++;
281     break;
282   case RP_GET_MULTIPLE_NEXT:
283     GNUNET_assert (uid != crc->first_uid);
284     crc->phase = RP_UPDATE;
285     break;
286   default:
287     GNUNET_break (0);
288     crc->phase = RP_ERROR;
289     break;
290   }
291   if (priority == get_priority (42))
292     crc->uid = uid;
293   GNUNET_SCHEDULER_add_continuation (&run_continuation,
294                                      crc, GNUNET_SCHEDULER_REASON_PREREQ_DONE);
295 }
296
297
298 static void
299 check_update (void *cls,
300               const GNUNET_HashCode * key,
301               size_t size,
302               const void *data,
303               enum GNUNET_BLOCK_Type type,
304               uint32_t priority,
305               uint32_t anonymity,
306               struct GNUNET_TIME_Absolute expiration, uint64_t uid)
307 {
308   struct CpsRunContext *crc = cls;
309
310   GNUNET_assert (key != NULL);
311   if ((anonymity == get_anonymity (42)) &&
312       (size == get_size (42)) && (priority == get_priority (42) + 100))
313     crc->phase = RP_DONE;
314   else
315   {
316     GNUNET_assert (size == get_size (43));
317     crc->offset++;
318   }
319   GNUNET_SCHEDULER_add_continuation (&run_continuation,
320                                      crc, GNUNET_SCHEDULER_REASON_PREREQ_DONE);
321 }
322
323
324 static void
325 run_continuation (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
326 {
327   struct CpsRunContext *crc = cls;
328
329   ok = (int) crc->phase;
330 #if VERBOSE
331   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test in phase %u\n", crc->phase);
332 #endif
333   switch (crc->phase)
334   {
335   case RP_PUT:
336 #if VERBOSE
337     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
338                 "Executing `%s' number %u\n", "PUT", crc->i);
339 #endif
340     GNUNET_CRYPTO_hash (&crc->i, sizeof (int), &crc->key);
341     GNUNET_DATASTORE_put (datastore,
342                           0,
343                           &crc->key,
344                           get_size (crc->i),
345                           get_data (crc->i),
346                           get_type (crc->i),
347                           get_priority (crc->i),
348                           get_anonymity (crc->i),
349                           0,
350                           get_expiration (crc->i),
351                           1, 1, TIMEOUT, &check_success, crc);
352     crc->i++;
353     if (crc->i == ITERATIONS)
354       crc->phase = RP_GET;
355     break;
356   case RP_GET:
357     crc->i--;
358 #if VERBOSE
359     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
360                 "Executing `%s' number %u\n", "GET", crc->i);
361 #endif
362     GNUNET_CRYPTO_hash (&crc->i, sizeof (int), &crc->key);
363     GNUNET_DATASTORE_get_key (datastore,
364                               crc->offset,
365                               &crc->key,
366                               get_type (crc->i),
367                               1, 1, TIMEOUT, &check_value, crc);
368     break;
369   case RP_DEL:
370     crc->i--;
371 #if VERBOSE
372     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
373                 "Executing `%s' number %u\n", "DEL", crc->i);
374 #endif
375     crc->data = NULL;
376     GNUNET_CRYPTO_hash (&crc->i, sizeof (int), &crc->key);
377     GNUNET_assert (NULL !=
378                    GNUNET_DATASTORE_get_key (datastore,
379                                              crc->offset,
380                                              &crc->key,
381                                              get_type (crc->i),
382                                              1, 1, TIMEOUT,
383                                              &delete_value, crc));
384     break;
385   case RP_DO_DEL:
386 #if VERBOSE
387     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
388                 "Executing `%s' number %u\n", "DO_DEL", crc->i);
389 #endif
390     if (crc->i == 0)
391     {
392       crc->i = ITERATIONS;
393       crc->phase = RP_DELVALIDATE;
394     }
395     else
396     {
397       crc->phase = RP_DEL;
398     }
399     GNUNET_assert (NULL !=
400                    GNUNET_DATASTORE_remove (datastore,
401                                             &crc->key,
402                                             crc->size,
403                                             crc->data,
404                                             1, 1, TIMEOUT,
405                                             &check_success, crc));
406     break;
407   case RP_DELVALIDATE:
408     crc->i--;
409 #if VERBOSE
410     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
411                 "Executing `%s' number %u\n", "DEL-VALIDATE", crc->i);
412 #endif
413     GNUNET_CRYPTO_hash (&crc->i, sizeof (int), &crc->key);
414     GNUNET_assert (NULL !=
415                    GNUNET_DATASTORE_get_key (datastore,
416                                              crc->offset,
417                                              &crc->key,
418                                              get_type (crc->i),
419                                              1, 1, TIMEOUT,
420                                              &check_nothing, crc));
421     break;
422   case RP_RESERVE:
423     crc->phase = RP_PUT_MULTIPLE;
424     GNUNET_DATASTORE_reserve (datastore,
425                               128 * 1024, 2, 1, 1, TIMEOUT, &get_reserved, crc);
426     break;
427   case RP_PUT_MULTIPLE:
428     crc->phase = RP_PUT_MULTIPLE_NEXT;
429     GNUNET_DATASTORE_put (datastore,
430                           crc->rid,
431                           &crc->key,
432                           get_size (42),
433                           get_data (42),
434                           get_type (42),
435                           get_priority (42),
436                           get_anonymity (42),
437                           0,
438                           get_expiration (42),
439                           1, 1, TIMEOUT, &check_success, crc);
440     break;
441   case RP_PUT_MULTIPLE_NEXT:
442     crc->phase = RP_GET_MULTIPLE;
443     GNUNET_DATASTORE_put (datastore,
444                           crc->rid,
445                           &crc->key,
446                           get_size (43),
447                           get_data (43),
448                           get_type (42),
449                           get_priority (43),
450                           get_anonymity (43),
451                           0,
452                           get_expiration (43),
453                           1, 1, TIMEOUT, &check_success, crc);
454     break;
455   case RP_GET_MULTIPLE:
456     GNUNET_assert (NULL !=
457                    GNUNET_DATASTORE_get_key (datastore,
458                                              crc->offset,
459                                              &crc->key,
460                                              get_type (42),
461                                              1, 1, TIMEOUT,
462                                              &check_multiple, crc));
463     break;
464   case RP_GET_MULTIPLE_NEXT:
465     GNUNET_assert (NULL !=
466                    GNUNET_DATASTORE_get_key (datastore,
467                                              crc->offset,
468                                              &crc->key,
469                                              get_type (42),
470                                              1, 1, TIMEOUT,
471                                              &check_multiple, crc));
472     break;
473   case RP_UPDATE:
474     GNUNET_assert (crc->uid > 0);
475     crc->phase = RP_UPDATE_VALIDATE;
476     GNUNET_DATASTORE_update (datastore,
477                              crc->uid,
478                              100,
479                              get_expiration (42),
480                              1, 1, TIMEOUT, &check_success, crc);
481     break;
482   case RP_UPDATE_VALIDATE:
483     GNUNET_assert (NULL !=
484                    GNUNET_DATASTORE_get_key (datastore,
485                                              crc->offset,
486                                              &crc->key,
487                                              get_type (42),
488                                              1, 1, TIMEOUT,
489                                              &check_update, crc));
490     break;
491   case RP_DONE:
492 #if VERBOSE
493     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Finished, disconnecting\n");
494 #endif
495     GNUNET_DATASTORE_disconnect (datastore, GNUNET_YES);
496     GNUNET_free (crc);
497     ok = 0;
498     break;
499   case RP_ERROR:
500     GNUNET_DATASTORE_disconnect (datastore, GNUNET_YES);
501     GNUNET_free (crc);
502     ok = 43;
503     break;
504   }
505 }
506
507
508 static void
509 run_tests (void *cls, int32_t success, const char *msg)
510 {
511   struct CpsRunContext *crc = cls;
512
513   switch (success)
514   {
515   case GNUNET_YES:
516     GNUNET_SCHEDULER_add_continuation (&run_continuation,
517                                        crc,
518                                        GNUNET_SCHEDULER_REASON_PREREQ_DONE);
519     return;
520   case GNUNET_NO:
521     fprintf (stderr, "Test 'put' operation failed, key already exists (!?)\n");
522     GNUNET_free (crc);
523     return;
524   case GNUNET_SYSERR:
525     fprintf (stderr,
526              "Test 'put' operation failed with error `%s' database likely not setup, skipping test.\n",
527              msg);
528     GNUNET_free (crc);
529     return;
530   default:
531     GNUNET_assert (0);
532   }
533 }
534
535
536 static void
537 run (void *cls,
538      char *const *args,
539      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
540 {
541   struct CpsRunContext *crc;
542   static GNUNET_HashCode zkey;
543
544   crc = GNUNET_malloc (sizeof (struct CpsRunContext));
545   crc->cfg = cfg;
546   crc->phase = RP_PUT;
547   now = GNUNET_TIME_absolute_get ();
548   datastore = GNUNET_DATASTORE_connect (cfg);
549   if (NULL ==
550       GNUNET_DATASTORE_put (datastore, 0,
551                             &zkey, 4, "TEST",
552                             GNUNET_BLOCK_TYPE_TEST,
553                             0, 0, 0,
554                             GNUNET_TIME_relative_to_absolute
555                             (GNUNET_TIME_UNIT_SECONDS), 0, 1,
556                             GNUNET_TIME_UNIT_MINUTES, &run_tests, crc))
557   {
558     fprintf (stderr, "Test 'put' operation failed.\n");
559     ok = 1;
560     GNUNET_free (crc);
561   }
562 }
563
564
565 static int
566 check ()
567 {
568   char cfg_name[128];
569
570 #if START_DATASTORE
571   struct GNUNET_OS_Process *proc;
572 #endif
573   char *const argv[] = {
574     "test-datastore-api",
575     "-c",
576     cfg_name,
577 #if VERBOSE
578     "-L", "DEBUG",
579 #endif
580     NULL
581   };
582   struct GNUNET_GETOPT_CommandLineOption options[] = {
583     GNUNET_GETOPT_OPTION_END
584   };
585   GNUNET_snprintf (cfg_name,
586                    sizeof (cfg_name),
587                    "test_datastore_api_data_%s.conf", plugin_name);
588 #if START_DATASTORE
589   proc = GNUNET_OS_start_process (NULL, NULL, "gnunet-service-arm",
590                                   "gnunet-service-arm",
591 #if VERBOSE
592                                   "-L", "DEBUG",
593 #endif
594                                   "-c", cfg_name, NULL);
595 #endif
596   GNUNET_assert (NULL != proc);
597   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
598                       argv, "test-datastore-api", "nohelp",
599                       options, &run, NULL);
600 #if START_DATASTORE
601   sleep (1);                    /* give datastore chance to receive 'DROP' request */
602   if (0 != GNUNET_OS_process_kill (proc, SIGTERM))
603   {
604     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
605     ok = 1;
606   }
607   GNUNET_OS_process_wait (proc);
608   GNUNET_OS_process_close (proc);
609   proc = NULL;
610 #endif
611   if (ok != 0)
612     fprintf (stderr, "Missed some testcases: %u\n", ok);
613   return ok;
614 }
615
616 int
617 main (int argc, char *argv[])
618 {
619   int ret;
620   char *pos;
621   char dir_name[128];
622
623   sleep (1);
624   /* determine name of plugin to use */
625   plugin_name = argv[0];
626   while (NULL != (pos = strstr (plugin_name, "_")))
627     plugin_name = pos + 1;
628   if (NULL != (pos = strstr (plugin_name, ".")))
629     pos[0] = 0;
630   else
631     pos = (char *) plugin_name;
632
633   GNUNET_snprintf (dir_name,
634                    sizeof (dir_name),
635                    "/tmp/test-gnunet-datastore-%s", plugin_name);
636   GNUNET_DISK_directory_remove (dir_name);
637   GNUNET_log_setup ("test-datastore-api",
638 #if VERBOSE
639                     "DEBUG",
640 #else
641                     "WARNING",
642 #endif
643                     NULL);
644   ret = check ();
645   if (pos != plugin_name)
646     pos[0] = '.';
647   GNUNET_DISK_directory_remove (dir_name);
648   return ret;
649 }
650
651 /* end of test_datastore_api.c */