multicast tests
[oweals/gnunet.git] / src / multicast / test_multicast.c
1 /*
2  * This file is part of GNUnet
3  * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 /**
22  * @file multicast/test_multicast.c
23  * @brief Tests for the Multicast API.
24  * @author Gabor X Toth
25  */
26
27 #include <inttypes.h>
28
29 #include "platform.h"
30 #include "gnunet_crypto_lib.h"
31 #include "gnunet_common.h"
32 #include "gnunet_util_lib.h"
33 #include "gnunet_testing_lib.h"
34 #include "gnunet_core_service.h"
35 #include "gnunet_multicast_service.h"
36
37 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
38
39 /**
40  * Return value from 'main'.
41  */
42 static int res;
43
44 /**
45  * Handle for task for timeout termination.
46  */
47 static struct GNUNET_SCHEDULER_Task * end_badly_task;
48
49 static const struct GNUNET_CONFIGURATION_Handle *cfg;
50
51 struct GNUNET_CORE_Handle *core;
52 struct GNUNET_PeerIdentity this_peer;
53
54 struct GNUNET_MULTICAST_Origin *origin;
55 struct GNUNET_MULTICAST_Member *member;
56
57 struct GNUNET_CRYPTO_EddsaPrivateKey *group_key;
58 struct GNUNET_CRYPTO_EddsaPublicKey group_pub_key;
59
60 struct GNUNET_CRYPTO_EcdsaPrivateKey *member_key;
61 struct GNUNET_CRYPTO_EcdsaPublicKey member_pub_key;
62
63 struct TransmitClosure {
64   struct GNUNET_MULTICAST_OriginTransmitHandle *orig_tmit;
65   struct GNUNET_MULTICAST_MemberTransmitHandle *mem_tmit;
66   char * data[16];
67   uint8_t data_delay[16];
68   uint8_t data_count;
69   uint8_t paused;
70   uint8_t n;
71 } tmit_cls;
72
73 struct OriginClosure {
74   uint8_t msgs_expected;
75   uint8_t n;
76 } origin_cls;
77
78 struct MemberClosure {
79   uint8_t msgs_expected;
80   size_t n;
81 } member_cls;
82
83 struct GNUNET_MessageHeader *join_req, *join_resp;
84
85 enum
86 {
87   TEST_NONE               = 0,
88   TEST_ORIGIN_START       = 1,
89   TEST_MEMBER_JOIN_REFUSE = 2,
90   TEST_MEMBER_JOIN_ADMIT  = 3,
91   TEST_ORIGIN_TO_ALL      = 4,
92   TEST_ORIGIN_TO_ALL_RECV = 5,
93   TEST_MEMBER_TO_ORIGIN   = 6,
94   TEST_MEMBER_PART        = 7,
95   TEST_ORIGIN_STOP        = 8,
96 } test;
97
98
99 static void
100 member_join (int t);
101
102
103 /**
104  * Clean up all resources used.
105  */
106 static void
107 cleanup ()
108 {
109   if (NULL != core)
110   {
111     GNUNET_CORE_disconnect (core);
112     core = NULL;
113   }
114   if (NULL != member)
115   {
116     GNUNET_MULTICAST_member_part (member, NULL, NULL);
117     member = NULL;
118   }
119   if (NULL != origin)
120   {
121     GNUNET_MULTICAST_origin_stop (origin, NULL, NULL);
122     origin = NULL;
123   }
124 }
125
126
127 /**
128  * Terminate the test case (failure).
129  *
130  * @param cls NULL
131  * @param tc scheduler context
132  */
133 static void
134 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
135 {
136   res = 1;
137   cleanup ();
138   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Test FAILED.\n");
139 }
140
141
142 /**
143  * Terminate the test case (success).
144  *
145  * @param cls NULL
146  * @param tc scheduler context
147  */
148 static void
149 end_normally (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
150 {
151   res = 0;
152   cleanup ();
153   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Test PASSED.\n");
154 }
155
156
157 /**
158  * Finish the test case (successfully).
159  */
160 static void
161 end ()
162 {
163   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ending tests.\n");
164
165   if (end_badly_task != NULL)
166   {
167     GNUNET_SCHEDULER_cancel (end_badly_task);
168     end_badly_task = NULL;
169   }
170   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
171                                 &end_normally, NULL);
172 }
173
174
175 void
176 tmit_resume (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
177 {
178   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmission resumed.\n");
179   struct TransmitClosure *tmit = cls;
180   if (NULL != tmit->orig_tmit)
181     GNUNET_MULTICAST_origin_to_all_resume (tmit->orig_tmit);
182   else
183     GNUNET_MULTICAST_member_to_origin_resume (tmit->mem_tmit);
184 }
185
186
187 static int
188 tmit_notify (void *cls, size_t *data_size, void *data)
189 {
190   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
191               "Test #%u: origin_tmit_notify()\n", test);
192   struct TransmitClosure *tmit = cls;
193
194   if (0 == tmit->data_count)
195   {
196     *data_size = 0;
197     return GNUNET_YES;
198   }
199
200   uint16_t size = strlen (tmit->data[tmit->n]);
201   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
202               "Transmit notify data: %u bytes available, "
203               "processing fragment %u/%u (size %u).\n",
204               *data_size, tmit->n + 1, tmit->data_count, size);
205   if (*data_size < size)
206   {
207     *data_size = 0;
208     GNUNET_assert (0);
209     return GNUNET_SYSERR;
210   }
211
212   if (GNUNET_YES != tmit->paused && 0 < tmit->data_delay[tmit->n])
213   {
214     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmission paused.\n");
215     tmit->paused = GNUNET_YES;
216     GNUNET_SCHEDULER_add_delayed (
217       GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
218                                      tmit->data_delay[tmit->n]),
219       tmit_resume, tmit);
220     *data_size = 0;
221     return GNUNET_NO;
222   }
223   tmit->paused = GNUNET_NO;
224
225   *data_size = size;
226   memcpy (data, tmit->data[tmit->n], size);
227
228   return ++tmit->n < tmit->data_count ? GNUNET_NO : GNUNET_YES;
229 }
230
231
232 static void
233 origin_recv_replay_msg (void *cls,
234                         const struct GNUNET_CRYPTO_EcdsaPublicKey *member_key,
235                         uint64_t message_id,
236                         uint64_t fragment_offset,
237                         uint64_t flags,
238                         struct GNUNET_MULTICAST_ReplayHandle *rh)
239 {
240   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
241               "Test #%u: origin_recv_replay_msg()\n", test);
242 }
243
244
245 static void
246 member_recv_replay_msg (void *cls,
247                         const struct GNUNET_CRYPTO_EcdsaPublicKey *member_key,
248                         uint64_t message_id,
249                         uint64_t fragment_offset,
250                         uint64_t flags,
251                         struct GNUNET_MULTICAST_ReplayHandle *rh)
252 {
253   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
254               "Test #%u: member_recv_replay_msg()\n", test);
255 }
256
257
258 static void
259 origin_recv_replay_frag (void *cls,
260                          const struct GNUNET_CRYPTO_EcdsaPublicKey *member_key,
261                          uint64_t fragment_id,
262                          uint64_t flags,
263                          struct GNUNET_MULTICAST_ReplayHandle *rh)
264 {
265   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
266               "Test #%u: origin_recv_replay_frag()\n", test);
267 }
268
269
270 static void
271 member_recv_replay_frag (void *cls,
272                          const struct GNUNET_CRYPTO_EcdsaPublicKey *member_key,
273                          uint64_t fragment_id,
274                          uint64_t flags,
275                          struct GNUNET_MULTICAST_ReplayHandle *rh)
276 {
277   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
278               "Test #%u: member_recv_replay_frag()\n", test);
279 }
280
281
282 static void
283 origin_recv_membership_test (void *cls,
284                              const struct GNUNET_CRYPTO_EcdsaPublicKey *member_key,
285                              uint64_t message_id,
286                              uint64_t group_generation,
287                              struct GNUNET_MULTICAST_MembershipTestHandle *mth)
288 {
289   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
290               "Test #%u: origin_recv_membership_test()\n", test);
291 }
292
293
294 static void
295 member_recv_membership_test (void *cls,
296                              const struct GNUNET_CRYPTO_EcdsaPublicKey *member_key,
297                              uint64_t message_id,
298                              uint64_t group_generation,
299                              struct GNUNET_MULTICAST_MembershipTestHandle *mth)
300 {
301   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
302               "Test #%u: member_recv_membership_test()\n", test);
303 }
304
305
306 static void
307 member_recv_join_request (void *cls,
308                           const struct GNUNET_CRYPTO_EcdsaPublicKey *member_key,
309                           const struct GNUNET_MessageHeader *join_msg,
310                           struct GNUNET_MULTICAST_JoinHandle *jh)
311 {
312   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
313               "Test #%u: member_recv_join_request()\n", test);
314 }
315
316
317 static void
318 origin_stopped (void *cls)
319 {
320   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
321               "Test #%u: origin_stopped()\n", test);
322   end ();
323 }
324
325
326 static void
327 schedule_origin_stop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
328 {
329   test = TEST_ORIGIN_STOP;
330   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
331               "Test #%u: origin_stop()\n", test);
332   GNUNET_MULTICAST_origin_stop (origin, origin_stopped, NULL);
333   origin = NULL;
334 }
335
336
337 static void
338 member_parted (void *cls)
339 {
340   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
341               "Test #%u: member_parted()\n", test);
342
343   switch (test)
344   {
345   case TEST_MEMBER_JOIN_REFUSE:
346     member_join (TEST_MEMBER_JOIN_ADMIT);
347     break;
348
349   case TEST_MEMBER_PART:
350     GNUNET_SCHEDULER_add_now (schedule_origin_stop, NULL);
351     break;
352
353   default:
354     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
355                 "Invalid test #%d in member_recv_join_decision()\n", test);
356     GNUNET_assert (0);
357   }
358 }
359
360
361 static void
362 member_part ()
363 {
364   test = TEST_MEMBER_PART;
365   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
366               "Test #%u: member_part()\n", test);
367   GNUNET_MULTICAST_member_part (member, member_parted, NULL);
368   member = NULL;
369 }
370
371
372 static void
373 schedule_member_part (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
374 {
375   GNUNET_MULTICAST_member_part (member, member_parted, NULL);
376 }
377
378
379 static void
380 origin_recv_request (void *cls,
381                      const struct GNUNET_MULTICAST_RequestHeader *req)
382 {
383   struct OriginClosure *ocls = cls;
384   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
385               "Test #%u: origin_recv_request()\n", test);
386   if (++ocls->n != ocls->msgs_expected)
387     return;
388   
389   GNUNET_assert (0 == memcmp (&req->member_key,
390                               &member_pub_key, sizeof (member_pub_key)));
391
392
393   // FIXME: check message content
394
395   member_part ();
396 }
397
398
399 static void
400 member_to_origin ()
401 {
402   test = TEST_MEMBER_TO_ORIGIN;
403   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
404               "Test #%u: member_to_origin()\n", test);
405
406   struct TransmitClosure *tmit = &tmit_cls;
407   *tmit = (struct TransmitClosure) {};
408   tmit->data[0] = "abc def";
409   tmit->data[1] = "ghi jkl mno";
410   tmit->data_delay[1] = 1;
411   tmit->data[2] = "pqr stuw xyz";
412   tmit->data_count = 3;
413
414   origin_cls.n = 0;
415   origin_cls.msgs_expected = 1;
416
417   GNUNET_MULTICAST_member_to_origin (member, 1, tmit_notify, tmit);
418 }
419
420
421 static void
422 member_recv_message (void *cls,
423                      const struct GNUNET_MULTICAST_MessageHeader *msg)
424 {
425   struct MemberClosure *mcls = cls;
426   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
427               "Test #%u: member_recv_message() %u/%u\n",
428               test, mcls->n + 1, mcls->msgs_expected);
429   if (++mcls->n != mcls->msgs_expected)
430     return;
431
432   // FIXME: check message content
433
434   switch (test)
435   {
436   case TEST_ORIGIN_TO_ALL_RECV:
437     member_to_origin ();
438     break;
439
440   default:
441     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
442                 "Invalid test #%d in origin_recv_message()\n", test);
443     GNUNET_assert (0);
444   }
445 }
446
447
448 static void
449 origin_recv_message (void *cls,
450                      const struct GNUNET_MULTICAST_MessageHeader *msg)
451 {
452   struct OriginClosure *ocls = cls;
453   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
454               "Test #%u: origin_recv_message() %u/%u\n",
455               test, ocls->n + 1, ocls->msgs_expected);
456   if (++ocls->n != ocls->msgs_expected)
457     return;
458
459   // FIXME: check message content
460
461   switch (test)
462   {
463   case TEST_ORIGIN_TO_ALL:
464     test = TEST_ORIGIN_TO_ALL_RECV;
465     break;
466
467   default:
468     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
469                 "Invalid test #%d in origin_recv_message()\n", test);
470     GNUNET_assert (0);
471   }
472 }
473
474
475 static void
476 origin_to_all ()
477 {
478   test = TEST_ORIGIN_TO_ALL;
479   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
480               "Test #%u: origin_to_all()\n", test);
481
482   struct TransmitClosure *tmit = &tmit_cls;
483   *tmit = (struct TransmitClosure) {};
484   tmit->data[0] = "ABC DEF";
485   tmit->data[1] = "GHI JKL MNO";
486   tmit->data_delay[1] = 1;
487   tmit->data[2] = "PQR STUW XYZ";
488   tmit->data_count = 3;
489
490   origin_cls.n = member_cls.n = 0;
491   origin_cls.msgs_expected = member_cls.msgs_expected = 1;
492
493   GNUNET_MULTICAST_origin_to_all (origin, 1, 1, tmit_notify, tmit);
494 }
495
496
497 static void
498 member_recv_join_decision (void *cls,
499                            int is_admitted,
500                            const struct GNUNET_PeerIdentity *peer,
501                            uint16_t relay_count,
502                            const struct GNUNET_PeerIdentity *relays,
503                            const struct GNUNET_MessageHeader *join_msg)
504 {
505   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
506               "Test #%u: member_recv_join_decision() - is_admitted: %d\n",
507               test, is_admitted);
508
509   GNUNET_assert (join_msg->size == join_resp->size);
510   GNUNET_assert (join_msg->type == join_resp->type);
511   GNUNET_assert (0 == memcmp (join_msg, join_resp, ntohs (join_resp->size)));
512
513   switch (test)
514   {
515   case TEST_MEMBER_JOIN_REFUSE:
516     GNUNET_assert (0 == relay_count);
517     GNUNET_SCHEDULER_add_now (schedule_member_part, NULL);
518     break;
519     
520   case TEST_MEMBER_JOIN_ADMIT:
521     GNUNET_assert (1 == relay_count);
522     GNUNET_assert (0 == memcmp (relays, &this_peer, sizeof (this_peer)));
523     origin_to_all ();
524     break;
525
526   default:
527     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
528                 "Invalid test #%d in member_recv_join_decision()\n", test);
529     GNUNET_assert (0);
530   }
531 }
532
533
534 static void
535 origin_recv_join_request (void *cls,
536                           const struct GNUNET_CRYPTO_EcdsaPublicKey *mem_key,
537                           const struct GNUNET_MessageHeader *join_msg,
538                           struct GNUNET_MULTICAST_JoinHandle *jh)
539 {
540   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
541               "Test #%u: origin_recv_join_request()\n", test);
542
543   GNUNET_assert (0 == memcmp (mem_key, &member_pub_key, sizeof (member_pub_key)));
544   GNUNET_assert (join_msg->size == join_req->size);
545   GNUNET_assert (join_msg->type == join_req->type);
546   GNUNET_assert (0 == memcmp (join_msg, join_req, ntohs (join_req->size)));
547
548   char data[] = "here's the decision";
549   uint8_t data_size = strlen (data) + 1;
550   join_resp = GNUNET_malloc (sizeof (join_resp) + data_size);
551   join_resp->size = htons (sizeof (join_resp) + data_size);
552   join_resp->type = htons (456);
553   memcpy (&join_resp[1], data, data_size);
554   
555   switch (test)
556   {
557   case TEST_MEMBER_JOIN_REFUSE:
558     GNUNET_MULTICAST_join_decision (jh, GNUNET_NO, 0, NULL, join_resp);
559     break;
560     
561   case TEST_MEMBER_JOIN_ADMIT:
562     GNUNET_MULTICAST_join_decision (jh, GNUNET_YES, 1, &this_peer, join_resp);
563     break;
564
565   default:
566     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
567                 "Invalid test #%d in origin_recv_join_request()\n", test);
568     GNUNET_assert (0);
569     break;
570   }
571 }
572
573
574 static void
575 member_join (int t)
576 {
577   test = t;
578   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
579               "Test #%u: member_join()\n", test);
580
581   member_key = GNUNET_CRYPTO_ecdsa_key_create ();
582   GNUNET_CRYPTO_ecdsa_key_get_public (member_key, &member_pub_key);
583
584   if (NULL != join_req)
585     GNUNET_free (join_req);
586
587   char data[] = "let me in!";
588   uint8_t data_size = strlen (data) + 1;
589   join_req = GNUNET_malloc (sizeof (join_req) + data_size);
590   join_req->size = htons (sizeof (join_req) + data_size);
591   join_req->type = htons (123);
592   memcpy (&join_req[1], data, data_size);
593
594   member = GNUNET_MULTICAST_member_join (cfg, &group_pub_key, member_key,
595                                          &this_peer, 1, &this_peer, join_req,
596                                          member_recv_join_request,
597                                          member_recv_join_decision,
598                                          member_recv_membership_test,
599                                          member_recv_replay_frag,
600                                          member_recv_replay_msg,
601                                          member_recv_message,
602                                          &member_cls);
603 }
604
605
606 static void
607 origin_start ()
608 {
609   test = TEST_ORIGIN_START;
610   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
611               "Test #%u: origin_start()\n", test);
612
613   group_key = GNUNET_CRYPTO_eddsa_key_create ();
614   GNUNET_CRYPTO_eddsa_key_get_public (group_key, &group_pub_key);
615
616   origin = GNUNET_MULTICAST_origin_start (cfg, group_key, 0,
617                                           origin_recv_join_request,
618                                           origin_recv_membership_test,
619                                           origin_recv_replay_frag,
620                                           origin_recv_replay_msg,
621                                           origin_recv_request,
622                                           origin_recv_message,
623                                           &origin_cls);
624   member_join (TEST_MEMBER_JOIN_REFUSE);
625 }
626
627
628 static void
629 core_connected (void *cls, const struct GNUNET_PeerIdentity *my_identity)
630 {
631   this_peer = *my_identity;
632   origin_start ();
633 }
634
635
636 /**
637  * Main function of the test, run from scheduler.
638  *
639  * @param cls NULL
640  * @param cfg configuration we use (also to connect to Multicast service)
641  * @param peer handle to access more of the peer (not used)
642  */
643 static void
644 #if DEBUG_TEST_MULTICAST
645 run (void *cls, char *const *args, const char *cfgfile,
646      const struct GNUNET_CONFIGURATION_Handle *c)
647 #else
648 run (void *cls,
649      const struct GNUNET_CONFIGURATION_Handle *c,
650      struct GNUNET_TESTING_Peer *peer)
651 #endif
652 {
653   cfg = c;
654   end_badly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
655
656   core = GNUNET_CORE_connect (cfg, NULL, &core_connected, NULL, NULL,
657                               NULL, GNUNET_NO, NULL, GNUNET_NO, NULL);
658 }
659
660
661 int
662 main (int argc, char *argv[])
663 {
664   res = 1;
665 #if DEBUG_TEST_MULTICAST
666   const struct GNUNET_GETOPT_CommandLineOption opts[] = {
667     GNUNET_GETOPT_OPTION_END
668   };
669   if (GNUNET_OK != GNUNET_PROGRAM_run (argc, argv, "test-multicast",
670                                        "test-multicast [options]",
671                                        opts, &run, NULL))
672     return 1;
673 #else
674   if (0 != GNUNET_TESTING_peer_run ("test-multicast", "test_multicast.conf", &run, NULL))
675     return 1;
676 #endif
677   return res;
678 }
679
680 /* end of test_multicast.c */