-Merge branch 'master' of ssh://gnunet.org/gnunet into gsoc2018/rest_api
[oweals/gnunet.git] / src / transport / transport-testing-main.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2016 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14     
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 /**
19  * @file transport-testing-main.c
20  * @brief convenience main function for tests
21  * @author Christian Grothoff
22  */
23 #include "transport-testing.h"
24
25
26 /**
27  * Closure for #connect_cb.
28  */
29 struct GNUNET_TRANSPORT_TESTING_ConnectRequestList
30 {
31   /**
32    * Stored in a DLL.
33    */
34   struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *next;
35
36   /**
37    * Stored in a DLL.
38    */
39   struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *prev;
40
41   /**
42    * Overall context we are in.
43    */
44   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc;
45
46   /**
47    * Connect request this is about.
48    */
49   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cr;
50
51   /**
52    * Peer being connected.
53    */
54   struct GNUNET_TRANSPORT_TESTING_PeerContext *p1;
55
56   /**
57    * Peer being connected.
58    */
59   struct GNUNET_TRANSPORT_TESTING_PeerContext *p2;
60
61 };
62
63
64 /**
65  * Shutdown function for the test. Stops all peers.
66  *
67  * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *`
68  */
69 static void
70 do_shutdown (void *cls)
71 {
72   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = cls;
73   struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *crl;
74
75   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
76               "Testcase shutting down\n");
77   if (NULL != ccc->shutdown_task)
78     ccc->shutdown_task (ccc->shutdown_task_cls);
79   if (NULL != ccc->timeout_task)
80   {
81     GNUNET_SCHEDULER_cancel (ccc->timeout_task);
82     ccc->timeout_task = NULL;
83   }
84   if (NULL != ccc->connect_task)
85   {
86     GNUNET_SCHEDULER_cancel (ccc->connect_task);
87     ccc->connect_task = NULL;
88   }
89   while (NULL != (crl = ccc->crl_head))
90   {
91     GNUNET_CONTAINER_DLL_remove (ccc->crl_head,
92                                  ccc->crl_tail,
93                                  crl);
94     GNUNET_TRANSPORT_TESTING_connect_peers_cancel (crl->cr);
95     GNUNET_free (crl);
96   }
97   for (unsigned int i=0;i<ccc->num_peers;i++)
98   {
99     if (NULL != ccc->p[i])
100     {
101       GNUNET_TRANSPORT_TESTING_stop_peer (ccc->p[i]);
102       ccc->p[i] = NULL;
103     }
104   }
105 }
106
107
108 /**
109  * Testcase hit timeout, shut it down with error.
110  *
111  * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *`
112  */
113 static void
114 do_timeout (void *cls)
115 {
116   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = cls;
117
118   ccc->timeout_task = NULL;
119   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
120               "Testcase timed out\n");
121   ccc->global_ret = GNUNET_SYSERR;
122   GNUNET_SCHEDULER_shutdown ();
123 }
124
125
126 /**
127  * Internal data structure.   Closure for
128  * #connect_cb, #disconnect_cb, #my_nc and #start_cb.
129  * Allows us to identify which peer this is about.
130  */
131 struct GNUNET_TRANSPORT_TESTING_InternalPeerContext
132 {
133   /**
134    * Overall context of the callback.
135    */
136   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc;
137
138   /**
139    * Offset of the peer this is about.
140    */
141   unsigned int off;
142 };
143
144
145 /**
146  * Information tracked per connected peer.
147  */
148 struct ConnectPairInfo
149 {
150   /**
151    * Peer this is about.
152    */
153   const struct GNUNET_PeerIdentity *sender;
154
155   /**
156    * Information about the receiving peer.
157    */
158   struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ipi;
159 };
160
161
162 /**
163  * Function called when we connected two peers.  Once we have gotten
164  * to the clique, launch test-specific logic.
165  *
166  * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *`
167  */
168 static void
169 connect_cb (void *cls)
170 {
171   struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *crl = cls;
172   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = crl->ccc;
173
174   GNUNET_CONTAINER_DLL_remove (ccc->crl_head,
175                                ccc->crl_tail,
176                                crl);
177   {
178     char *p1_c = GNUNET_strdup (GNUNET_i2s (&crl->p1->id));
179
180     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
181                 "Peers connected: %u (%s) <-> %u (%s)\n",
182                 crl->p1->no,
183                 p1_c,
184                 crl->p2->no,
185                 GNUNET_i2s (&crl->p2->id));
186     GNUNET_free (p1_c);
187     GNUNET_free (crl);
188   }
189   if (NULL == ccc->crl_head)
190   {
191     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
192                 "All connections UP, launching custom test logic.\n");
193     GNUNET_SCHEDULER_add_now (ccc->connect_continuation,
194                               ccc->connect_continuation_cls);
195   }
196 }
197
198
199 /**
200  * Find peer by peer ID.
201  *
202  * @param ccc context to search
203  * @param peer peer to look for
204  * @return NULL if @a peer was not found
205  */
206 struct GNUNET_TRANSPORT_TESTING_PeerContext *
207 GNUNET_TRANSPORT_TESTING_find_peer (struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc,
208                                     const struct GNUNET_PeerIdentity *peer)
209 {
210   for (unsigned int i=0;i<ccc->num_peers;i++)
211     if ( (NULL != ccc->p[i]) &&
212          (0 == memcmp (peer,
213                        &ccc->p[i]->id,
214                        sizeof (*peer))) )
215       return ccc->p[i];
216   return NULL;
217 }
218
219
220 /**
221  * Wrapper around peers connecting.  Calls client's nc function.
222  *
223  * @param cls our `struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *`
224  * @param peer peer we got connected to
225  * @param mq message queue for transmissions to @a peer
226  * @return closure for message handlers
227  */
228 static void *
229 my_nc (void *cls,
230        const struct GNUNET_PeerIdentity *peer,
231        struct GNUNET_MQ_Handle *mq)
232 {
233   struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ipi = cls;
234   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = ipi->ccc;
235   struct ConnectPairInfo *cpi;
236
237   if (NULL != ccc->nc)
238     ccc->nc (ccc->cls,
239              ccc->p[ipi->off],
240              peer);
241   cpi = GNUNET_new (struct ConnectPairInfo);
242   cpi->ipi = ipi;
243   cpi->sender = peer; /* valid until disconnect */
244   return cpi;
245 }
246
247
248 /**
249  * Wrapper around peers disconnecting.  Calls client's nd function.
250  *
251  * @param cls our `struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *`
252  * @param peer peer we got disconnected from
253  * @param custom_cls return value from @my_nc
254  */
255 static void
256 my_nd (void *cls,
257        const struct GNUNET_PeerIdentity *peer,
258        void *custom_cls)
259 {
260   struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ipi = cls;
261   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = ipi->ccc;
262   struct ConnectPairInfo *cpi = custom_cls;
263
264   if (NULL != ccc->nd)
265     ccc->nd (ccc->cls,
266              ccc->p[ipi->off],
267              peer);
268   GNUNET_free (cpi);
269 }
270
271
272 /**
273  * Wrapper around receiving data.  Calls client's rec function.
274  *
275  * @param cls our `struct ConnectPairInfo *`
276  * @param message message we received
277  * @return #GNUNET_OK (all messages are fine)
278  */
279 static int
280 check_test (void *cls,
281             const struct GNUNET_TRANSPORT_TESTING_TestMessage *message)
282 {
283   return GNUNET_OK;
284 }
285
286
287 /**
288  * Wrapper around receiving data.  Calls client's rec function.
289  *
290  * @param cls our `struct ConnectPairInfo *`
291  * @param message message we received
292  */
293 static void
294 handle_test (void *cls,
295              const struct GNUNET_TRANSPORT_TESTING_TestMessage *message)
296 {
297   struct ConnectPairInfo *cpi = cls;
298   struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ipi = cpi->ipi;
299   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = ipi->ccc;
300
301   if (NULL != ccc->rec)
302     ccc->rec (ccc->cls,
303               ccc->p[ipi->off],
304               cpi->sender,
305               message);
306 }
307
308
309 /**
310  * Wrapper around receiving data.  Calls client's rec function.
311  *
312  * @param cls our `struct ConnectPairInfo *`
313  * @param message message we received
314  * @return #GNUNET_OK (all messages are fine)
315  */
316 static int
317 check_test2 (void *cls,
318              const struct GNUNET_TRANSPORT_TESTING_TestMessage *message)
319 {
320   return GNUNET_OK;
321 }
322
323
324 /**
325  * Wrapper around receiving data.  Calls client's rec function.
326  *
327  * @param cls our `struct ConnectPairInfo *`
328  * @param message message we received
329  */
330 static void
331 handle_test2 (void *cls,
332               const struct GNUNET_TRANSPORT_TESTING_TestMessage *message)
333 {
334   struct ConnectPairInfo *cpi = cls;
335   struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ipi = cpi->ipi;
336   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = ipi->ccc;
337
338   if (NULL != ccc->rec)
339     ccc->rec (ccc->cls,
340               ccc->p[ipi->off],
341               cpi->sender,
342               message);
343 }
344
345
346 /**
347  * Connect the peers as a clique.
348  *
349  * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext`
350  */
351 static void
352 do_connect (void *cls)
353 {
354   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = cls;
355
356   ccc->connect_task = NULL;
357   for (unsigned int i=0;i<ccc->num_peers;i++)
358     for (unsigned int j=(ccc->bi_directional ? 0 : i+1);j<ccc->num_peers;j++)
359     {
360       struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *crl;
361
362       if (i == j)
363         continue;
364       crl = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_ConnectRequestList);
365       GNUNET_CONTAINER_DLL_insert (ccc->crl_head,
366                                    ccc->crl_tail,
367                                    crl);
368       crl->ccc = ccc;
369       crl->p1 = ccc->p[i];
370       crl->p2 = ccc->p[j];
371       {
372         char *sender_c = GNUNET_strdup (GNUNET_i2s (&ccc->p[0]->id));
373
374         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
375                     "Test tries to connect peer %u (`%s') -> peer %u (`%s')\n",
376                     ccc->p[0]->no,
377                     sender_c,
378                     ccc->p[1]->no,
379                     GNUNET_i2s (&ccc->p[1]->id));
380         GNUNET_free (sender_c);
381       }
382       crl->cr = GNUNET_TRANSPORT_TESTING_connect_peers (ccc->p[i],
383                                                         ccc->p[j],
384                                                         &connect_cb,
385                                                         crl);
386     }
387 }
388
389
390 /**
391  * Function called once we have successfully launched a peer.
392  * Once all peers have been launched, we connect all of them
393  * in a clique.
394  *
395  * @param cls our `struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *`
396  */
397 static void
398 start_cb (void *cls)
399 {
400   struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ipi = cls;
401   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = ipi->ccc;
402   struct GNUNET_TRANSPORT_TESTING_PeerContext *p = ccc->p[ipi->off];
403
404   ccc->started++;
405   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
406               "Peer %u (`%s') started\n",
407               p->no,
408               GNUNET_i2s (&p->id));
409   if (ccc->started != ccc->num_peers)
410     return;
411   if (NULL != ccc->pre_connect_task)
412   {
413     /* Run the custom per-connect job, then give it a second to
414        go into effect before we continue connecting peers. */
415     ccc->pre_connect_task (ccc->pre_connect_task_cls);
416     ccc->connect_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
417                                                       &do_connect,
418                                                       ccc);
419   }
420   else
421   {
422     do_connect (ccc);
423   }
424 }
425
426
427 /**
428  * Function run from #GNUNET_TRANSPORT_TESTING_connect_check
429  * once the scheduler is up.  Should launch the peers and
430  * then in the continuations try to connect them.
431  *
432  * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *`
433  * @param args ignored
434  * @param cfgfile ignored
435  * @param cfg configuration
436  */
437 static void
438 connect_check_run (void *cls,
439                    char *const *args,
440                    const char *cfgfile,
441                    const struct GNUNET_CONFIGURATION_Handle *cfg)
442 {
443   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = cls;
444   int ok;
445
446   ccc->cfg = cfg;
447   ccc->timeout_task = GNUNET_SCHEDULER_add_delayed (ccc->timeout,
448                                                     &do_timeout,
449                                                     ccc);
450   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
451                                  ccc);
452   ok = GNUNET_OK;
453   for (unsigned int i=0;i<ccc->num_peers;i++)
454   {
455     struct GNUNET_MQ_MessageHandler handlers[] = {
456       GNUNET_MQ_hd_var_size (test,
457                              GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE,
458                              struct GNUNET_TRANSPORT_TESTING_TestMessage,
459                              NULL),
460       GNUNET_MQ_hd_var_size (test2,
461                              GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE2,
462                              struct GNUNET_TRANSPORT_TESTING_TestMessage,
463                              NULL),
464       GNUNET_MQ_handler_end()
465     };
466     ccc->p[i] = GNUNET_TRANSPORT_TESTING_start_peer (ccc->tth,
467                                                      ccc->cfg_files[i],
468                                                      i + 1,
469                                                      handlers,
470                                                      &my_nc,
471                                                      &my_nd,
472                                                      &ccc->ip[i],
473                                                      &start_cb,
474                                                      &ccc->ip[i]);
475     if (NULL == ccc->p[i])
476       ok = GNUNET_SYSERR;
477   }
478   if (GNUNET_OK != ok)
479   {
480     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
481                 "Fail! Could not start peers!\n");
482     GNUNET_SCHEDULER_shutdown ();
483   }
484 }
485
486
487 /**
488  * Common implementation of the #GNUNET_TRANSPORT_TESTING_CheckCallback.
489  * Starts and connects the two peers, then invokes the
490  * `connect_continuation` from @a cls.  Sets up a timeout to
491  * abort the test, and a shutdown handler to clean up properly
492  * on exit.
493  *
494  * @param cls closure of type `struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext`
495  * @param tth_ initialized testing handle
496  * @param test_plugin_ name of the plugin
497  * @param test_name_ name of the test
498  * @param num_peers number of entries in the @a cfg_file array
499  * @param cfg_files array of names of configuration files for the peers
500  * @return #GNUNET_SYSERR on error
501  */
502 int
503 GNUNET_TRANSPORT_TESTING_connect_check (void *cls,
504                                         struct GNUNET_TRANSPORT_TESTING_Handle *tth_,
505                                         const char *test_plugin_,
506                                         const char *test_name_,
507                                         unsigned int num_peers,
508                                         char *cfg_files[])
509 {
510   static struct GNUNET_GETOPT_CommandLineOption options[] = {
511     GNUNET_GETOPT_OPTION_END
512   };
513   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = cls;
514   struct GNUNET_TRANSPORT_TESTING_PeerContext *p[num_peers];
515   struct GNUNET_TRANSPORT_TESTING_InternalPeerContext ip[num_peers];
516   char * argv[] = {
517     (char *) test_name_,
518     "-c",
519     (char *) ccc->config_file,
520     NULL
521   };
522
523   ccc->num_peers = num_peers;
524   ccc->cfg_files = cfg_files;
525   ccc->test_plugin = test_plugin_;
526   ccc->test_name = test_name_;
527   ccc->tth = tth_;
528   ccc->global_ret = GNUNET_OK;
529   ccc->p = p;
530   ccc->ip = ip;
531   for (unsigned int i=0;i<num_peers;i++)
532   {
533     ip[i].off = i;
534     ip[i].ccc = ccc;
535   }
536   if (GNUNET_OK !=
537       GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
538                           argv,
539                           test_name_,
540                           "nohelp",
541                           options,
542                           &connect_check_run,
543                           ccc))
544     return GNUNET_SYSERR;
545   return ccc->global_ret;
546 }
547
548
549 /**
550  * Setup testcase.  Calls @a check with the data the test needs.
551  *
552  * @param argv0 binary name (argv[0])
553  * @param filename source file name (__FILE__)
554  * @param num_peers number of peers to start
555  * @param check main function to run
556  * @param check_cls closure for @a check
557  * @return #GNUNET_OK on success
558  */
559 int
560 GNUNET_TRANSPORT_TESTING_main_ (const char *argv0,
561                                 const char *filename,
562                                 unsigned int num_peers,
563                                 GNUNET_TRANSPORT_TESTING_CheckCallback check,
564                                 void *check_cls)
565 {
566   struct GNUNET_TRANSPORT_TESTING_Handle *tth;
567   char *test_name;
568   char *test_source;
569   char *test_plugin;
570   char *cfg_names[num_peers];
571   int ret;
572
573   ret = GNUNET_OK;
574   test_name = GNUNET_TRANSPORT_TESTING_get_test_name (argv0);
575   GNUNET_log_setup (test_name,
576                     "WARNING",
577                     NULL);
578   test_source = GNUNET_TRANSPORT_TESTING_get_test_source_name (filename);
579   test_plugin = GNUNET_TRANSPORT_TESTING_get_test_plugin_name (argv0,
580                                                                test_source);
581   for (unsigned int i=0;i<num_peers;i++)
582     cfg_names[i] = GNUNET_TRANSPORT_TESTING_get_config_name (argv0,
583                                                              i+1);
584   tth = GNUNET_TRANSPORT_TESTING_init ();
585   if (NULL == tth)
586   {
587     ret = GNUNET_SYSERR;
588   }
589   else
590   {
591     ret = check (check_cls,
592                  tth,
593                  test_plugin,
594                  test_name,
595                  num_peers,
596                  cfg_names);
597     GNUNET_TRANSPORT_TESTING_done (tth);
598   }
599   for (unsigned int i=0;i<num_peers;i++)
600     GNUNET_free (cfg_names[i]);
601   GNUNET_free (test_source);
602   GNUNET_free_non_null (test_plugin);
603   GNUNET_free (test_name);
604   return ret;
605 }
606
607 /* end of transport-testing-main.c */