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