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