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