migrate more tests to new transmission 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
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  * @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  * Function called when we connected two peers.
149  * Once we have gotten to the clique, launch
150  * test-specific logic.
151  *
152  * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *`
153  */
154 static void
155 connect_cb (void *cls)
156 {
157   struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *crl = cls;
158   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = crl->ccc;
159
160   GNUNET_CONTAINER_DLL_remove (ccc->crl_head,
161                                ccc->crl_tail,
162                                crl);
163   {
164     char *p1_c = GNUNET_strdup (GNUNET_i2s (&crl->p1->id));
165
166     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
167                 "Peers connected: %u (%s) <-> %u (%s)\n",
168                 crl->p1->no,
169                 p1_c,
170                 crl->p2->no,
171                 GNUNET_i2s (&crl->p2->id));
172     GNUNET_free (p1_c);
173     GNUNET_free (crl);
174   }
175   if (NULL == ccc->crl_head)
176   {
177     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
178                 "All connections UP, launching custom test logic.\n");
179     GNUNET_SCHEDULER_add_now (ccc->connect_continuation,
180                               ccc->connect_continuation_cls);
181   }
182 }
183
184
185 /**
186  * Find peer by peer ID.
187  *
188  * @param ccc context to search
189  * @param peer peer to look for
190  * @return NULL if @a peer was not found
191  */
192 struct GNUNET_TRANSPORT_TESTING_PeerContext *
193 GNUNET_TRANSPORT_TESTING_find_peer (struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc,
194                                     const struct GNUNET_PeerIdentity *peer)
195 {
196   for (unsigned int i=0;i<ccc->num_peers;i++)
197     if ( (NULL != ccc->p[i]) &&
198          (0 == memcmp (peer,
199                        &ccc->p[i]->id,
200                        sizeof (*peer))) )
201       return ccc->p[i];
202   return NULL;
203 }
204
205
206 /**
207  * Wrapper around peers connecting.  Calls client's nc function.
208  *
209  * @param cls our `struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *`
210  * @param peer peer we got connected to
211  */
212 static void
213 my_nc (void *cls,
214        const struct GNUNET_PeerIdentity *peer)
215 {
216   struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ipi = cls;
217   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = ipi->ccc;
218
219   if (NULL != ccc->nc)
220     ccc->nc (ccc->cls,
221              ccc->p[ipi->off],
222              peer);
223 }
224
225
226
227 /**
228  * Wrapper around peers disconnecting.  Calls client's nd function.
229  *
230  * @param cls our `struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *`
231  * @param peer peer we got disconnected from
232  */
233 static void
234 my_nd (void *cls,
235        const struct GNUNET_PeerIdentity *peer)
236 {
237   struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ipi = cls;
238   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = ipi->ccc;
239
240   if (NULL != ccc->nd)
241     ccc->nd (ccc->cls,
242              ccc->p[ipi->off],
243              peer);
244 }
245
246
247 /**
248  * Wrapper around receiving data.  Calls client's rec function.
249  *
250  * @param cls our `struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *`
251  * @param peer peer we got a message from
252  * @param message message we received
253  */
254 static void
255 my_rec (void *cls,
256         const struct GNUNET_PeerIdentity *peer,
257         const struct GNUNET_MessageHeader *message)
258 {
259   struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ipi = cls;
260   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = ipi->ccc;
261
262   if (NULL != ccc->rec)
263     ccc->rec (ccc->cls,
264               ccc->p[ipi->off],
265               peer,
266               message);
267 }
268
269
270 /**
271  * Connect the peers as a clique.
272  *
273  * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext`
274  */
275 static void
276 do_connect (void *cls)
277 {
278   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = cls;
279
280   ccc->connect_task = NULL;
281   for (unsigned int i=0;i<ccc->num_peers;i++)
282     for (unsigned int j=(ccc->bi_directional ? 0 : i+1);j<ccc->num_peers;j++)
283     {
284       struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *crl;
285
286       if (i == j)
287         continue;
288       crl = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_ConnectRequestList);
289       GNUNET_CONTAINER_DLL_insert (ccc->crl_head,
290                                    ccc->crl_tail,
291                                    crl);
292       crl->ccc = ccc;
293       crl->p1 = ccc->p[i];
294       crl->p2 = ccc->p[j];
295       {
296         char *sender_c = GNUNET_strdup (GNUNET_i2s (&ccc->p[0]->id));
297
298         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
299                     "Test tries to connect peer %u (`%s') -> peer %u (`%s')\n",
300                     ccc->p[0]->no,
301                     sender_c,
302                     ccc->p[1]->no,
303                     GNUNET_i2s (&ccc->p[1]->id));
304         GNUNET_free (sender_c);
305       }
306       crl->cr = GNUNET_TRANSPORT_TESTING_connect_peers (ccc->p[i],
307                                                         ccc->p[j],
308                                                         &connect_cb,
309                                                         crl);
310     }
311 }
312
313
314 /**
315  * Function called once we have successfully launched a peer.
316  * Once all peers have been launched, we connect all of them
317  * in a clique.
318  *
319  * @param p peer that was launched (redundant, kill ASAP)
320  * @param cls our `struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *`
321  */
322 static void
323 start_cb (struct GNUNET_TRANSPORT_TESTING_PeerContext *p,
324           void *cls)
325 {
326   struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ipi = cls;
327   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = ipi->ccc;
328
329   ccc->started++;
330   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
331               "Peer %u (`%s') started\n",
332               p->no,
333               GNUNET_i2s (&p->id));
334   if (ccc->started != ccc->num_peers)
335     return;
336   if (NULL != ccc->pre_connect_task)
337   {
338     /* Run the custom per-connect job, then give it a second to
339        go into effect before we continue connecting peers. */
340     ccc->pre_connect_task (ccc->pre_connect_task_cls);
341     ccc->connect_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
342                                                       &do_connect,
343                                                       ccc);
344   }
345   else
346   {
347     do_connect (ccc);
348   }
349 }
350
351
352 /**
353  * Function run from #GNUNET_TRANSPORT_TESTING_connect_check
354  * once the scheduler is up.  Should launch the peers and
355  * then in the continuations try to connect them.
356  *
357  * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *`
358  * @param args ignored
359  * @param cfgfile ignored
360  * @param cfg configuration
361  */
362 static void
363 connect_check_run (void *cls,
364                    char *const *args,
365                    const char *cfgfile,
366                    const struct GNUNET_CONFIGURATION_Handle *cfg)
367 {
368   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = cls;
369   int ok;
370
371   ccc->cfg = cfg;
372   ccc->timeout_task = GNUNET_SCHEDULER_add_delayed (ccc->timeout,
373                                                     &do_timeout,
374                                                     ccc);
375   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
376                                  ccc);
377   ok = GNUNET_OK;
378   for (unsigned int i=0;i<ccc->num_peers;i++)
379   {
380     ccc->p[i] = GNUNET_TRANSPORT_TESTING_start_peer (ccc->tth,
381                                                      ccc->cfg_files[i],
382                                                      i + 1,
383                                                      &my_rec,
384                                                      &my_nc,
385                                                      &my_nd,
386                                                      &ccc->ip[i],
387                                                      &start_cb,
388                                                      &ccc->ip[i]);
389     if (NULL == ccc->p[i])
390       ok = GNUNET_SYSERR;
391   }
392   if (GNUNET_OK != ok)
393   {
394     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
395                 "Fail! Could not start peers!\n");
396     GNUNET_SCHEDULER_shutdown ();
397   }
398 }
399
400
401 /**
402  * Common implementation of the #GNUNET_TRANSPORT_TESTING_CheckCallback.
403  * Starts and connects the two peers, then invokes the
404  * `connect_continuation` from @a cls.  Sets up a timeout to
405  * abort the test, and a shutdown handler to clean up properly
406  * on exit.
407  *
408  * @param cls closure of type `struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext`
409  * @param tth_ initialized testing handle
410  * @param test_plugin_ name of the plugin
411  * @param test_name_ name of the test
412  * @param num_peers number of entries in the @a cfg_file array
413  * @param cfg_files array of names of configuration files for the peers
414  * @return #GNUNET_SYSERR on error
415  */
416 int
417 GNUNET_TRANSPORT_TESTING_connect_check (void *cls,
418                                         struct GNUNET_TRANSPORT_TESTING_Handle *tth_,
419                                         const char *test_plugin_,
420                                         const char *test_name_,
421                                         unsigned int num_peers,
422                                         char *cfg_files[])
423 {
424   static struct GNUNET_GETOPT_CommandLineOption options[] = {
425     GNUNET_GETOPT_OPTION_END
426   };
427   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = cls;
428   struct GNUNET_TRANSPORT_TESTING_PeerContext *p[num_peers];
429   struct GNUNET_TRANSPORT_TESTING_InternalPeerContext ip[num_peers];
430   char * argv[] = {
431     (char *) test_name_,
432     "-c",
433     (char *) ccc->config_file,
434     NULL
435   };
436
437   ccc->num_peers = num_peers;
438   ccc->cfg_files = cfg_files;
439   ccc->test_plugin = test_plugin_;
440   ccc->test_name = test_name_;
441   ccc->tth = tth_;
442   ccc->global_ret = GNUNET_OK;
443   ccc->p = p;
444   ccc->ip = ip;
445   for (unsigned int i=0;i<num_peers;i++)
446   {
447     ip[i].off = i;
448     ip[i].ccc = ccc;
449   }
450   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
451                       argv,
452                       test_name_,
453                       "nohelp",
454                       options,
455                       &connect_check_run,
456                       ccc);
457   return ccc->global_ret;
458 }
459
460
461 /**
462  * Setup testcase.  Calls @a check with the data the test needs.
463  *
464  * @param argv0 binary name (argv[0])
465  * @param filename source file name (__FILE__)
466  * @param num_peers number of peers to start
467  * @param check main function to run
468  * @param check_cls closure for @a check
469  * @return #GNUNET_OK on success
470  */
471 int
472 GNUNET_TRANSPORT_TESTING_main_ (const char *argv0,
473                                 const char *filename,
474                                 unsigned int num_peers,
475                                 GNUNET_TRANSPORT_TESTING_CheckCallback check,
476                                 void *check_cls)
477 {
478   struct GNUNET_TRANSPORT_TESTING_Handle *tth;
479   char *test_name;
480   char *test_source;
481   char *test_plugin;
482   char *cfg_names[num_peers];
483   int ret;
484
485   ret = GNUNET_OK;
486   test_name = GNUNET_TRANSPORT_TESTING_get_test_name (argv0);
487   GNUNET_log_setup (test_name,
488                     "WARNING",
489                     NULL);
490   test_source = GNUNET_TRANSPORT_TESTING_get_test_source_name (filename);
491   test_plugin = GNUNET_TRANSPORT_TESTING_get_test_plugin_name (argv0,
492                                                                test_source);
493   for (unsigned int i=0;i<num_peers;i++)
494     cfg_names[i] = GNUNET_TRANSPORT_TESTING_get_config_name (argv0,
495                                                              i+1);
496   tth = GNUNET_TRANSPORT_TESTING_init ();
497   if (NULL == tth)
498   {
499     ret = GNUNET_SYSERR;
500   }
501   else
502   {
503     ret = check (check_cls,
504                  tth,
505                  test_plugin,
506                  test_name,
507                  num_peers,
508                  cfg_names);
509     GNUNET_TRANSPORT_TESTING_done (tth);
510   }
511   for (unsigned int i=0;i<num_peers;i++)
512     GNUNET_free (cfg_names[i]);
513   GNUNET_free (test_source);
514   GNUNET_free (test_plugin);
515   GNUNET_free (test_name);
516   return ret;
517 }
518
519 /* end of transport-testing-main.c */