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