better API
[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                             get_expiration (crc->i),
406                             1, 1, TIMEOUT,
407                             &check_success,
408                             crc);
409       crc->i++;
410       if (crc->i == ITERATIONS)
411         crc->phase = RP_GET;
412       break;
413     case RP_GET:
414       crc->i--;
415 #if VERBOSE
416       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
417                   "Executing `%s' number %u\n",
418                   "GET",
419                   crc->i);
420 #endif
421       GNUNET_CRYPTO_hash (&crc->i, sizeof (int), &crc->key);
422       GNUNET_DATASTORE_get (datastore, 
423                             &crc->key,
424                             get_type (crc->i),
425                             1, 1, TIMEOUT,
426                             &check_value,
427                             crc);
428       break;
429     case RP_DEL:
430       crc->i--;
431 #if VERBOSE
432       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
433                   "Executing `%s' number %u\n",
434                   "DEL",
435                   crc->i);
436 #endif
437       crc->data = NULL;
438       GNUNET_CRYPTO_hash (&crc->i, sizeof (int), &crc->key);
439       GNUNET_DATASTORE_get (datastore, 
440                             &crc->key,
441                             get_type (crc->i),
442                             1, 1, TIMEOUT,
443                             &delete_value,
444                             crc);
445       break;
446     case RP_DO_DEL:
447 #if VERBOSE
448       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
449                   "Executing `%s' number %u\n",
450                   "DO_DEL",
451                   crc->i);
452 #endif
453       if (crc->i == 0)
454         {
455           crc->i = ITERATIONS;   
456           crc->phase = RP_DELVALIDATE;
457         }      
458       else
459         {
460           crc->phase = RP_DEL;
461         }
462       GNUNET_DATASTORE_remove (datastore,
463                                &crc->key,
464                                crc->size,
465                                crc->data,
466                                1, 1, TIMEOUT,
467                                &check_success,
468                                crc);
469       break;   
470     case RP_DELVALIDATE:
471       crc->i--;
472 #if VERBOSE
473       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
474                   "Executing `%s' number %u\n",
475                   "DEL-VALIDATE",
476                   crc->i);
477 #endif
478       GNUNET_CRYPTO_hash (&crc->i, sizeof (int), &crc->key);
479       GNUNET_DATASTORE_get (datastore, 
480                             &crc->key,
481                             get_type (crc->i),
482                             1, 1, TIMEOUT,
483                             &check_nothing,
484                             crc);
485       break;
486     case RP_RESERVE:
487       crc->phase = RP_PUT_MULTIPLE;
488       GNUNET_DATASTORE_reserve (datastore,
489                                 128*1024,
490                                 2,
491                                 1, 1, TIMEOUT,
492                                 &get_reserved,
493                                 crc);
494       break;
495     case RP_PUT_MULTIPLE:
496       crc->phase = RP_PUT_MULTIPLE_NEXT;
497       GNUNET_DATASTORE_put (datastore,
498                             crc->rid,
499                             &crc->key,
500                             get_size (42),
501                             get_data (42),
502                             get_type (42),
503                             get_priority (42),
504                             get_anonymity (42),
505                             get_expiration (42),
506                             1, 1, TIMEOUT,
507                             &check_success,
508                             crc);
509       break;
510     case RP_PUT_MULTIPLE_NEXT:
511       crc->phase = RP_GET_MULTIPLE;
512       GNUNET_DATASTORE_put (datastore,
513                             crc->rid,
514                             &crc->key,
515                             get_size (43),
516                             get_data (43),
517                             get_type (42),
518                             get_priority (43),
519                             get_anonymity (43),
520                             get_expiration (43),
521                             1, 1, TIMEOUT,
522                             &check_success,
523                             crc);
524       break;
525     case RP_GET_MULTIPLE:
526       GNUNET_DATASTORE_get (datastore,
527                             &crc->key, 
528                             get_type (42),
529                             1, 1, TIMEOUT,
530                             &check_multiple,
531                             crc);
532       break;
533     case RP_GET_MULTIPLE_NEXT:
534     case RP_GET_MULTIPLE_DONE:
535       GNUNET_assert (0);
536       break;
537     case RP_UPDATE:
538       GNUNET_assert (crc->uid > 0);
539       crc->phase = RP_UPDATE_VALIDATE;
540       GNUNET_DATASTORE_update (datastore,
541                                crc->uid,
542                                100,
543                                get_expiration (42),
544                                1, 1, TIMEOUT,
545                                &check_success,
546                                crc);
547       break;
548     case RP_UPDATE_VALIDATE:
549       GNUNET_DATASTORE_get (datastore,
550                             &crc->key, 
551                             get_type (42),
552                             1, 1, TIMEOUT,
553                             &check_update,
554                             crc);   
555       break;
556     case RP_UPDATE_DONE:
557       GNUNET_assert (0);
558       break;
559     case RP_DONE:
560 #if VERBOSE
561       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
562                   "Finished, disconnecting\n");
563 #endif
564       GNUNET_DATASTORE_disconnect (datastore, GNUNET_YES);
565       GNUNET_free (crc);
566       ok = 0;
567       break;
568     case RP_ERROR:
569       GNUNET_DATASTORE_disconnect (datastore, GNUNET_YES);
570       GNUNET_free (crc);
571       ok = 43;
572       break;
573     }
574 }
575
576
577 static void
578 run_tests (void *cls,
579            int success,
580            const char *msg)
581 {
582   struct CpsRunContext *crc = cls;
583
584   if (success != GNUNET_YES)
585     {
586       fprintf (stderr,
587                "Test 'put' operation failed with error `%s' database likely not setup, skipping test.",
588                msg);
589       GNUNET_free (crc);
590       return;
591     }
592   GNUNET_SCHEDULER_add_continuation (&run_continuation,
593                                      crc,
594                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
595 }
596
597
598 static void
599 run (void *cls,
600      char *const *args,
601      const char *cfgfile,
602      const struct GNUNET_CONFIGURATION_Handle *cfg)
603 {
604   struct CpsRunContext *crc;
605   static GNUNET_HashCode zkey;
606
607   crc = GNUNET_malloc(sizeof(struct CpsRunContext));
608   crc->cfg = cfg;
609   crc->phase = RP_PUT;
610   now = GNUNET_TIME_absolute_get ();
611   datastore = GNUNET_DATASTORE_connect (cfg);
612   if (NULL ==
613       GNUNET_DATASTORE_put (datastore, 0,
614                             &zkey, 4, "TEST",
615                             GNUNET_BLOCK_TYPE_TEST,
616                             0, 0, GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_SECONDS),
617                             0, 1, GNUNET_TIME_UNIT_MINUTES,
618                             &run_tests, crc))
619     {
620       fprintf (stderr,
621                "Test 'put' operation failed.\n");
622       ok = 1;
623       GNUNET_free (crc);
624     }
625 }
626
627
628 static int
629 check ()
630 {
631   char cfg_name[128];
632 #if START_DATASTORE
633   struct GNUNET_OS_Process *proc;
634 #endif
635   char *const argv[] = {
636     "test-datastore-api",
637     "-c",
638     cfg_name,
639 #if VERBOSE
640     "-L", "DEBUG",
641 #endif
642     NULL
643   };
644   struct GNUNET_GETOPT_CommandLineOption options[] = {
645     GNUNET_GETOPT_OPTION_END
646   };
647   GNUNET_snprintf (cfg_name,
648                    sizeof (cfg_name),
649                    "test_datastore_api_data_%s.conf",
650                    plugin_name);
651 #if START_DATASTORE
652   proc = GNUNET_OS_start_process (NULL, NULL, "gnunet-service-arm",
653                                  "gnunet-service-arm",
654 #if VERBOSE
655                                  "-L", "DEBUG",
656 #endif
657                                  "-c", cfg_name, NULL);
658 #endif
659   GNUNET_assert (NULL != proc);
660   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
661                       argv, "test-datastore-api", "nohelp",
662                       options, &run, NULL);
663 #if START_DATASTORE
664   if (0 != GNUNET_OS_process_kill (proc, SIGTERM))
665     {
666       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
667       ok = 1;
668     }
669   GNUNET_OS_process_wait (proc);
670   GNUNET_OS_process_close (proc);
671   proc = NULL;
672 #endif
673   if (ok != 0)
674     fprintf (stderr, "Missed some testcases: %u\n", ok);
675   return ok;
676 }
677
678 int
679 main (int argc, char *argv[])
680 {
681   int ret;
682   char *pos;
683   char dir_name[128];
684
685   /* determine name of plugin to use */
686   plugin_name = argv[0];
687   while (NULL != (pos = strstr(plugin_name, "_")))
688     plugin_name = pos+1;
689   if (NULL != (pos = strstr(plugin_name, ".")))
690     pos[0] = 0;
691   else
692     pos = (char *) plugin_name;
693
694   GNUNET_snprintf (dir_name,
695                    sizeof (dir_name),
696                    "/tmp/test-gnunet-datastore-%s",
697                    plugin_name);
698   GNUNET_DISK_directory_remove (dir_name);
699   GNUNET_log_setup ("test-datastore-api",
700 #if VERBOSE
701                     "DEBUG",
702 #else
703                     "WARNING",
704 #endif
705                     NULL);
706   ret = check ();
707   if (pos != plugin_name)
708     pos[0] = '.';
709   GNUNET_DISK_directory_remove (dir_name);
710   return ret;
711 }
712
713 /* end of test_datastore_api.c */