965f05687908353ec7ad6326b342ab15d7a038da
[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   memset (buf, i, 8 * i);
68   return buf;
69 }
70
71
72 static int
73 get_type(int i)
74 {
75   return i+1;
76 }
77
78
79 static int 
80 get_priority (int i)
81 {
82   return i+1;
83 }
84
85
86 static int
87 get_anonymity(int i)
88 {
89   return i;
90 }
91
92
93 static struct GNUNET_TIME_Absolute 
94 get_expiration (int i)
95 {
96   struct GNUNET_TIME_Absolute av;
97
98   av.abs_value = now.abs_value + 20000000 - i * 1000;
99   return av;
100 }
101
102 enum RunPhase
103   {
104     RP_DONE = 0,
105     RP_PUT,
106     RP_GET,
107     RP_DEL,
108     RP_DO_DEL,
109     RP_DELVALIDATE,
110     RP_RESERVE,
111     RP_PUT_MULTIPLE,
112     RP_PUT_MULTIPLE_NEXT,
113     RP_GET_MULTIPLE,
114     RP_GET_MULTIPLE_NEXT, /* 10 */
115     RP_GET_MULTIPLE_DONE,
116     RP_UPDATE,
117     RP_UPDATE_VALIDATE, /* 13 */
118     RP_UPDATE_DONE,
119     RP_ERROR
120   };
121
122
123 struct CpsRunContext
124 {
125   GNUNET_HashCode key;
126   int i;
127   int rid;
128   const struct GNUNET_CONFIGURATION_Handle *cfg;
129   void *data;
130   size_t size;
131   enum RunPhase phase;
132   unsigned long long uid;
133 };
134
135
136 static void
137 run_continuation (void *cls,
138                   const struct GNUNET_SCHEDULER_TaskContext *tc);
139
140
141 static void
142 check_success (void *cls,
143                int success,
144                const char *msg)
145 {
146   struct CpsRunContext *crc = cls;
147   if (GNUNET_OK != success)
148     {
149       ok = 42;
150       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
151                   "%s\n", msg);
152       GNUNET_SCHEDULER_shutdown ();
153       return;
154     }
155   GNUNET_free_non_null (crc->data);
156   crc->data = NULL;
157   GNUNET_SCHEDULER_add_continuation (&run_continuation,
158                                      crc,
159                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
160 }
161
162
163 static void
164 get_reserved (void *cls,
165               int success,
166               const char *msg)
167 {
168   struct CpsRunContext *crc = cls;
169   if (0 >= success)
170     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
171                 "%s\n", msg);
172   GNUNET_assert (0 < success);
173   crc->rid = success;
174   GNUNET_SCHEDULER_add_continuation (&run_continuation,
175                                      crc,
176                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
177 }
178
179
180 static void 
181 check_value (void *cls,
182              const GNUNET_HashCode * key,
183              size_t size,
184              const void *data,
185              enum GNUNET_BLOCK_Type type,
186              uint32_t priority,
187              uint32_t anonymity,
188              struct GNUNET_TIME_Absolute
189              expiration, uint64_t uid)
190 {
191   struct CpsRunContext *crc = cls;
192   int i;
193
194   if (key == NULL)
195     {
196       if (crc->i == 0)
197         {
198           crc->phase = RP_DEL;
199           crc->i = ITERATIONS;
200         }
201       GNUNET_SCHEDULER_add_continuation (&run_continuation,
202                                          crc,
203                                          GNUNET_SCHEDULER_REASON_PREREQ_DONE);
204       return;
205     }
206   i = crc->i;
207   GNUNET_assert (size == get_size (i));
208   GNUNET_assert (0 == memcmp (data, get_data(i), size));
209   GNUNET_assert (type == get_type (i));
210   GNUNET_assert (priority == get_priority (i));
211   GNUNET_assert (anonymity == get_anonymity(i));
212   GNUNET_assert (expiration.abs_value == get_expiration(i).abs_value);
213   GNUNET_DATASTORE_get_next (datastore);
214 }
215
216
217 static void 
218 delete_value (void *cls,
219               const GNUNET_HashCode * key,
220               size_t size,
221               const void *data,
222               enum GNUNET_BLOCK_Type type,
223               uint32_t priority,
224               uint32_t anonymity,
225               struct GNUNET_TIME_Absolute
226               expiration, uint64_t uid)
227 {
228   struct CpsRunContext *crc = cls;
229   if (key == NULL)
230     {
231       if (crc->data == NULL)
232         {
233           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
234                       "Content %u not found!\n",
235                       crc->i);
236           crc->phase = RP_ERROR;
237         }
238       else
239         {
240           crc->phase = RP_DO_DEL;
241         }
242       GNUNET_SCHEDULER_add_continuation (&run_continuation,
243                                          crc,
244                                          GNUNET_SCHEDULER_REASON_PREREQ_DONE);
245       return;
246     }
247   GNUNET_assert (crc->data == NULL);
248   crc->size = size;
249   crc->key = *key;
250   crc->data = GNUNET_malloc (size);
251   memcpy (crc->data, data, size);
252   GNUNET_DATASTORE_get_next (datastore);
253 }
254
255
256 static void 
257 check_nothing (void *cls,
258                const GNUNET_HashCode * key,
259                size_t size,
260                const void *data,
261                enum GNUNET_BLOCK_Type type,
262                uint32_t priority,
263                uint32_t anonymity,
264                struct GNUNET_TIME_Absolute
265                expiration, uint64_t uid)
266 {
267   struct CpsRunContext *crc = cls;
268   GNUNET_assert (key == NULL);
269   if (crc->i == 0)
270     {
271       crc->phase = RP_RESERVE;    
272     }
273   GNUNET_SCHEDULER_add_continuation (&run_continuation,
274                                      crc,
275                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
276 }
277
278
279 static void 
280 check_multiple (void *cls,
281                 const GNUNET_HashCode * key,
282                 size_t size,
283                 const void *data,
284                 enum GNUNET_BLOCK_Type type,
285                 uint32_t priority,
286                 uint32_t anonymity,
287                 struct GNUNET_TIME_Absolute
288                 expiration, uint64_t uid)
289 {
290   struct CpsRunContext *crc = cls;
291
292   if (key == NULL)
293     {
294       if (crc->phase != RP_GET_MULTIPLE_DONE)
295         {
296           fprintf (stderr, 
297                    "Wrong phase: %d\n",
298                    crc->phase);
299           GNUNET_break (0);
300           crc->phase = RP_ERROR;
301         }
302       else
303         {
304           crc->phase = RP_UPDATE;
305         }
306       GNUNET_SCHEDULER_add_continuation (&run_continuation,
307                                          crc,
308                                          GNUNET_SCHEDULER_REASON_PREREQ_DONE);
309       return;
310     }
311   switch (crc->phase)
312     {
313     case RP_GET_MULTIPLE:
314       crc->phase = RP_GET_MULTIPLE_NEXT;
315       break;
316     case RP_GET_MULTIPLE_NEXT:
317       crc->phase = RP_GET_MULTIPLE_DONE;
318       break;
319     case RP_GET_MULTIPLE_DONE:
320       /* do not advance further */
321       break;
322     default:
323       GNUNET_break (0);
324       break;
325     }
326 #if VERBOSE
327   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
328               "Test in phase %u\n", crc->phase);
329 #endif
330   if (priority == get_priority (42))
331     crc->uid = uid;
332   GNUNET_DATASTORE_get_next (datastore);
333 }
334
335
336 static void 
337 check_update (void *cls,
338               const GNUNET_HashCode * key,
339               size_t size,
340               const void *data,
341               enum GNUNET_BLOCK_Type type,
342               uint32_t priority,
343               uint32_t anonymity,
344               struct GNUNET_TIME_Absolute
345               expiration, uint64_t uid)
346 {
347   struct CpsRunContext *crc = cls;
348
349   if (key == NULL)
350     {
351       if (crc->phase != RP_UPDATE_DONE)
352         {
353           GNUNET_break (0);
354           crc->phase = RP_ERROR;
355         }
356       else
357         {
358           crc->phase = RP_DONE;
359         }
360       GNUNET_SCHEDULER_add_continuation (&run_continuation,
361                                          crc,
362                                          GNUNET_SCHEDULER_REASON_PREREQ_DONE);
363       return;
364     }
365   if ( (anonymity == get_anonymity (42)) &&
366        (size == get_size (42)) &&
367        (priority == get_priority (42) + 100) )
368     {
369       crc->phase = RP_UPDATE_DONE;
370     }
371   else
372     GNUNET_assert (size == get_size (43));
373   GNUNET_DATASTORE_get_next (datastore);
374 }
375
376
377 static void
378 run_continuation (void *cls,
379                   const struct GNUNET_SCHEDULER_TaskContext *tc)
380 {
381   struct CpsRunContext *crc = cls;
382   ok = (int) crc->phase;
383 #if VERBOSE
384   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
385               "Test in phase %u\n", crc->phase);
386 #endif
387   switch (crc->phase)
388     {
389     case RP_PUT:
390 #if VERBOSE
391       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
392                   "Executing `%s' number %u\n",
393                   "PUT",
394                   crc->i);
395 #endif
396       GNUNET_CRYPTO_hash (&crc->i, sizeof (int), &crc->key);
397       GNUNET_DATASTORE_put (datastore,
398                             0,
399                             &crc->key,
400                             get_size (crc->i),
401                             get_data (crc->i),
402                             get_type (crc->i),
403                             get_priority (crc->i),
404                             get_anonymity (crc->i),
405                             0,
406                             get_expiration (crc->i),
407                             1, 1, TIMEOUT,
408                             &check_success,
409                             crc);
410       crc->i++;
411       if (crc->i == ITERATIONS)
412         crc->phase = RP_GET;
413       break;
414     case RP_GET:
415       crc->i--;
416 #if VERBOSE
417       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
418                   "Executing `%s' number %u\n",
419                   "GET",
420                   crc->i);
421 #endif
422       GNUNET_CRYPTO_hash (&crc->i, sizeof (int), &crc->key);
423       GNUNET_DATASTORE_get (datastore, 
424                             &crc->key,
425                             get_type (crc->i),
426                             1, 1, TIMEOUT,
427                             &check_value,
428                             crc);
429       break;
430     case RP_DEL:
431       crc->i--;
432 #if VERBOSE
433       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
434                   "Executing `%s' number %u\n",
435                   "DEL",
436                   crc->i);
437 #endif
438       crc->data = NULL;
439       GNUNET_CRYPTO_hash (&crc->i, sizeof (int), &crc->key);
440       GNUNET_DATASTORE_get (datastore, 
441                             &crc->key,
442                             get_type (crc->i),
443                             1, 1, TIMEOUT,
444                             &delete_value,
445                             crc);
446       break;
447     case RP_DO_DEL:
448 #if VERBOSE
449       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
450                   "Executing `%s' number %u\n",
451                   "DO_DEL",
452                   crc->i);
453 #endif
454       if (crc->i == 0)
455         {
456           crc->i = ITERATIONS;   
457           crc->phase = RP_DELVALIDATE;
458         }      
459       else
460         {
461           crc->phase = RP_DEL;
462         }
463       GNUNET_DATASTORE_remove (datastore,
464                                &crc->key,
465                                crc->size,
466                                crc->data,
467                                1, 1, TIMEOUT,
468                                &check_success,
469                                crc);
470       break;   
471     case RP_DELVALIDATE:
472       crc->i--;
473 #if VERBOSE
474       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
475                   "Executing `%s' number %u\n",
476                   "DEL-VALIDATE",
477                   crc->i);
478 #endif
479       GNUNET_CRYPTO_hash (&crc->i, sizeof (int), &crc->key);
480       GNUNET_DATASTORE_get (datastore, 
481                             &crc->key,
482                             get_type (crc->i),
483                             1, 1, TIMEOUT,
484                             &check_nothing,
485                             crc);
486       break;
487     case RP_RESERVE:
488       crc->phase = RP_PUT_MULTIPLE;
489       GNUNET_DATASTORE_reserve (datastore,
490                                 128*1024,
491                                 2,
492                                 1, 1, TIMEOUT,
493                                 &get_reserved,
494                                 crc);
495       break;
496     case RP_PUT_MULTIPLE:
497       crc->phase = RP_PUT_MULTIPLE_NEXT;
498       GNUNET_DATASTORE_put (datastore,
499                             crc->rid,
500                             &crc->key,
501                             get_size (42),
502                             get_data (42),
503                             get_type (42),
504                             get_priority (42),
505                             get_anonymity (42),
506                             0,
507                             get_expiration (42),
508                             1, 1, TIMEOUT,
509                             &check_success,
510                             crc);
511       break;
512     case RP_PUT_MULTIPLE_NEXT:
513       crc->phase = RP_GET_MULTIPLE;
514       GNUNET_DATASTORE_put (datastore,
515                             crc->rid,
516                             &crc->key,
517                             get_size (43),
518                             get_data (43),
519                             get_type (42),
520                             get_priority (43),
521                             get_anonymity (43),
522                             0,
523                             get_expiration (43),
524                             1, 1, TIMEOUT,
525                             &check_success,
526                             crc);
527       break;
528     case RP_GET_MULTIPLE:
529       GNUNET_DATASTORE_get (datastore,
530                             &crc->key, 
531                             get_type (42),
532                             1, 1, TIMEOUT,
533                             &check_multiple,
534                             crc);
535       break;
536     case RP_GET_MULTIPLE_NEXT:
537     case RP_GET_MULTIPLE_DONE:
538       GNUNET_assert (0);
539       break;
540     case RP_UPDATE:
541       GNUNET_assert (crc->uid > 0);
542       crc->phase = RP_UPDATE_VALIDATE;
543       GNUNET_DATASTORE_update (datastore,
544                                crc->uid,
545                                100,
546                                get_expiration (42),
547                                1, 1, TIMEOUT,
548                                &check_success,
549                                crc);
550       break;
551     case RP_UPDATE_VALIDATE:
552       GNUNET_DATASTORE_get (datastore,
553                             &crc->key, 
554                             get_type (42),
555                             1, 1, TIMEOUT,
556                             &check_update,
557                             crc);   
558       break;
559     case RP_UPDATE_DONE:
560       GNUNET_assert (0);
561       break;
562     case RP_DONE:
563 #if VERBOSE
564       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
565                   "Finished, disconnecting\n");
566 #endif
567       GNUNET_DATASTORE_disconnect (datastore, GNUNET_YES);
568       GNUNET_free (crc);
569       ok = 0;
570       break;
571     case RP_ERROR:
572       GNUNET_DATASTORE_disconnect (datastore, GNUNET_YES);
573       GNUNET_free (crc);
574       ok = 43;
575       break;
576     }
577 }
578
579
580 static void
581 run_tests (void *cls,
582            int success,
583            const char *msg)
584 {
585   struct CpsRunContext *crc = cls;
586
587   if (success != GNUNET_YES)
588     {
589       fprintf (stderr,
590                "Test 'put' operation failed with error `%s' database likely not setup, skipping test.",
591                msg);
592       GNUNET_free (crc);
593       return;
594     }
595   GNUNET_SCHEDULER_add_continuation (&run_continuation,
596                                      crc,
597                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
598 }
599
600
601 static void
602 run (void *cls,
603      char *const *args,
604      const char *cfgfile,
605      const struct GNUNET_CONFIGURATION_Handle *cfg)
606 {
607   struct CpsRunContext *crc;
608   static GNUNET_HashCode zkey;
609
610   crc = GNUNET_malloc(sizeof(struct CpsRunContext));
611   crc->cfg = cfg;
612   crc->phase = RP_PUT;
613   now = GNUNET_TIME_absolute_get ();
614   datastore = GNUNET_DATASTORE_connect (cfg);
615   if (NULL ==
616       GNUNET_DATASTORE_put (datastore, 0,
617                             &zkey, 4, "TEST",
618                             GNUNET_BLOCK_TYPE_TEST,
619                             0, 0, 0, GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_SECONDS),
620                             0, 1, GNUNET_TIME_UNIT_MINUTES,
621                             &run_tests, crc))
622     {
623       fprintf (stderr,
624                "Test 'put' operation failed.\n");
625       ok = 1;
626       GNUNET_free (crc);
627     }
628 }
629
630
631 static int
632 check ()
633 {
634   char cfg_name[128];
635 #if START_DATASTORE
636   struct GNUNET_OS_Process *proc;
637 #endif
638   char *const argv[] = {
639     "test-datastore-api",
640     "-c",
641     cfg_name,
642 #if VERBOSE
643     "-L", "DEBUG",
644 #endif
645     NULL
646   };
647   struct GNUNET_GETOPT_CommandLineOption options[] = {
648     GNUNET_GETOPT_OPTION_END
649   };
650   GNUNET_snprintf (cfg_name,
651                    sizeof (cfg_name),
652                    "test_datastore_api_data_%s.conf",
653                    plugin_name);
654 #if START_DATASTORE
655   proc = GNUNET_OS_start_process (NULL, NULL, "gnunet-service-arm",
656                                  "gnunet-service-arm",
657 #if VERBOSE
658                                  "-L", "DEBUG",
659 #endif
660                                  "-c", cfg_name, NULL);
661 #endif
662   GNUNET_assert (NULL != proc);
663   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
664                       argv, "test-datastore-api", "nohelp",
665                       options, &run, NULL);
666 #if START_DATASTORE
667   if (0 != GNUNET_OS_process_kill (proc, SIGTERM))
668     {
669       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
670       ok = 1;
671     }
672   GNUNET_OS_process_wait (proc);
673   GNUNET_OS_process_close (proc);
674   proc = NULL;
675 #endif
676   if (ok != 0)
677     fprintf (stderr, "Missed some testcases: %u\n", ok);
678   return ok;
679 }
680
681 int
682 main (int argc, char *argv[])
683 {
684   int ret;
685   char *pos;
686   char dir_name[128];
687
688   /* determine name of plugin to use */
689   plugin_name = argv[0];
690   while (NULL != (pos = strstr(plugin_name, "_")))
691     plugin_name = pos+1;
692   if (NULL != (pos = strstr(plugin_name, ".")))
693     pos[0] = 0;
694   else
695     pos = (char *) plugin_name;
696
697   GNUNET_snprintf (dir_name,
698                    sizeof (dir_name),
699                    "/tmp/test-gnunet-datastore-%s",
700                    plugin_name);
701   GNUNET_DISK_directory_remove (dir_name);
702   GNUNET_log_setup ("test-datastore-api",
703 #if VERBOSE
704                     "DEBUG",
705 #else
706                     "WARNING",
707 #endif
708                     NULL);
709   ret = check ();
710   if (pos != plugin_name)
711     pos[0] = '.';
712   GNUNET_DISK_directory_remove (dir_name);
713   return ret;
714 }
715
716 /* end of test_datastore_api.c */