longer timeout to make test work on slower machines, but the problem is still that...
[oweals/gnunet.git] / src / testing / testing_group.c
1 /*
2       This file is part of GNUnet
3       (C) 2008, 2009 Christian Grothoff (and other contributing authors)
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 2, 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., 59 Temple Place - Suite 330,
18       Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * @file testing/testing_group.c
23  * @brief convenience API for writing testcases for GNUnet
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_arm_service.h"
28 #include "gnunet_testing_lib.h"
29
30 /**
31  * Lowest port used for GNUnet testing.  Should be high enough to not
32  * conflict with other applications running on the hosts but be low
33  * enough to not conflict with client-ports (typically starting around
34  * 32k).
35  */
36 #define LOW_PORT 10000
37
38 /**
39  * Highest port used for GNUnet testing.  Should be low enough to not
40  * conflict with the port range for "local" ports (client apps; see
41  * /proc/sys/net/ipv4/ip_local_port_range on Linux for example).
42  */
43 #define HIGH_PORT 32000
44
45 /**
46  * Data we keep per peer.
47  */
48 struct PeerData
49 {
50   /**
51    * (Initial) configuration of the host.
52    * (initial because clients could change
53    *  it and we would not know about those
54    *  updates).
55    */
56   struct GNUNET_CONFIGURATION_Handle *cfg;
57   
58   /**
59    * Handle for controlling the daemon.
60    */
61   struct GNUNET_TESTING_Daemon *daemon;
62 };
63
64
65 /**
66  * Data we keep per host.
67  */
68 struct HostData
69 {
70   /**
71    * Name of the host.
72    */
73   char *hostname;
74   
75   /**
76    * Lowest port that we have not yet used
77    * for GNUnet.
78    */
79   uint16_t minport;
80 };
81
82
83 /**
84  * Handle to a group of GNUnet peers.
85  */
86 struct GNUNET_TESTING_PeerGroup
87 {
88   /**
89    * Our scheduler.
90    */ 
91   struct GNUNET_SCHEDULER_Handle *sched;
92
93   /**
94    * Configuration template.
95    */
96   const struct GNUNET_CONFIGURATION_Handle *cfg;
97
98   /**
99    * Function to call on each started daemon.
100    */ 
101   GNUNET_TESTING_NotifyDaemonRunning cb;
102
103   /**
104    * Closure for cb.
105    */
106   void *cb_cls;
107
108   /**
109    * NULL-terminated array of information about
110    * hosts.
111    */
112   struct HostData *hosts;
113
114   /**
115    * Array of "total" peers.
116    */
117   struct PeerData *peers;
118
119   /**
120    * Number of peers in this group.
121    */ 
122   unsigned int total;
123
124 };
125
126
127 struct UpdateContext 
128 {
129   struct GNUNET_CONFIGURATION_Handle *ret;
130   unsigned int nport;
131 };
132
133 /**
134  * Function to iterate over options.  Copies
135  * the options to the target configuration,
136  * updating PORT values as needed.
137  *
138  * @param cls closure
139  * @param section name of the section
140  * @param option name of the option
141  * @param value value of the option
142  */
143 static void 
144 update_config(void *cls,
145               const char *section,
146               const char *option,
147               const char *value)
148 {
149   struct UpdateContext *ctx = cls;
150   unsigned int ival;
151   char cval[12];
152
153   if ( (0 == strcmp (option, "PORT")) &&
154        (1 == sscanf (value, "%u", &ival)) )
155     {
156       GNUNET_snprintf (cval,
157                        sizeof(cval),
158                        "%u",
159                        ctx->nport++);
160       value = cval;
161     }              
162   GNUNET_CONFIGURATION_set_value_string (ctx->ret,
163                                          section,
164                                          option,
165                                          value);
166 }
167
168
169 /**
170  * Create a new configuration using the given configuration
171  * as a template; however, each PORT in the existing cfg
172  * must be renumbered by incrementing "*port".  If we run
173  * out of "*port" numbers, return NULL. 
174  * 
175  * @param cfg template configuration
176  * @param port port numbers to use, update to reflect
177  *             port numbers that were used
178  * @return new configuration, NULL on error
179  */
180 static struct GNUNET_CONFIGURATION_Handle*
181 make_config (const struct GNUNET_CONFIGURATION_Handle*cfg,
182              uint16_t *port)
183 {
184   struct UpdateContext uc;
185   uint16_t orig;
186
187   orig = *port;
188   uc.nport = *port;
189   uc.ret = GNUNET_CONFIGURATION_create ();
190   GNUNET_CONFIGURATION_iterate (cfg,
191                                 &update_config,
192                                 &uc);
193   if (uc.nport >= HIGH_PORT)
194     {
195       *port = orig;
196       GNUNET_CONFIGURATION_destroy (uc.ret);
197       return NULL;
198     }
199   *port = (uint16_t) uc.nport;
200   return uc.ret;
201 }
202
203
204 /**
205  * Start count gnunetd processes with the same set of transports and
206  * applications.  The port numbers (any option called "PORT") will be
207  * adjusted to ensure that no two peers running on the same system
208  * have the same port(s) in their respective configurations.
209  *
210  * @param sched scheduler to use 
211  * @param cfg configuration template to use
212  * @param total number of daemons to start
213  * @param cb function to call on each daemon that was started
214  * @param cb_cls closure for cb
215  * @param hostnames space-separated list of hostnames to use; can be NULL (to run
216  *        everything on localhost).
217  * @return NULL on error, otherwise handle to control peer group
218  */
219 struct GNUNET_TESTING_PeerGroup *
220 GNUNET_TESTING_daemons_start (struct GNUNET_SCHEDULER_Handle *sched,
221                               const struct GNUNET_CONFIGURATION_Handle *cfg,
222                               unsigned int total,
223                               GNUNET_TESTING_NotifyDaemonRunning cb,
224                               void *cb_cls,
225                               const char *hostnames)
226 {
227   struct GNUNET_TESTING_PeerGroup *pg;
228   const char *rpos;
229   char *pos;
230   char *start;
231   const char *hostname;
232   struct GNUNET_CONFIGURATION_Handle *pcfg;
233   unsigned int off;
234   unsigned int hostcnt;
235   uint16_t minport;
236
237   if (0 == total)
238     {
239       GNUNET_break (0);
240       return NULL;
241     }
242   pg = GNUNET_malloc (sizeof(struct GNUNET_TESTING_PeerGroup));
243   pg->sched = sched;
244   pg->cfg = cfg;
245   pg->cb = cb;
246   pg->cb_cls = cb_cls;
247   pg->total = total;
248   pg->peers = GNUNET_malloc (total * sizeof(struct PeerData));
249   if (NULL != hostnames)
250     {
251       off = 2;
252       /* skip leading spaces */
253       while ( (0 != *hostnames) &&
254               (isspace(*hostnames)))
255         hostnames++;
256       rpos = hostnames;
257       while ('\0' != *rpos)
258         {
259           if (isspace (*rpos))
260             off++;
261           rpos++;
262         }
263       pg->hosts = GNUNET_malloc (off * sizeof (struct HostData));
264       off = 0;
265       start = GNUNET_strdup (hostnames);
266       pos = start;
267       while ('\0' != *pos)
268         {
269           if (isspace (*pos))
270             {
271               *pos = '\0';
272               if (strlen(start) > 0)
273                 {
274                   pg->hosts[off].minport = LOW_PORT;
275                   pg->hosts[off++].hostname = start;
276                 }
277               start = pos+1;
278             }
279           pos++;
280         }
281       if (strlen(start) > 0)
282         {
283           pg->hosts[off].minport = LOW_PORT;
284           pg->hosts[off++].hostname = start;
285         }
286       if (off == 0)
287         {
288           GNUNET_free (start);
289           GNUNET_free (pg->hosts);
290           pg->hosts = NULL;
291         }
292       hostcnt = off;
293       minport = 0; /* make gcc happy */
294     }
295   else
296     {
297       hostcnt = 0;
298       minport = LOW_PORT;
299     }
300   for (off = 0; off < total; off++)
301     {
302       if (hostcnt > 0)
303         {
304           hostname = pg->hosts[off % hostcnt].hostname;
305           pcfg = make_config (cfg, &pg->hosts[off % hostcnt].minport);
306         }
307       else
308         {
309           hostname = NULL;
310           pcfg = make_config (cfg, &minport);
311         }
312       if (NULL == pcfg)
313         {
314           GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 
315                       _("Could not create configuration for peer number %u on `%s'!\n"),
316                       off,
317                       hostname == NULL ? "localhost" : hostname);
318           continue;
319         }
320       pg->peers[off].cfg = pcfg;
321       pg->peers[off].daemon = GNUNET_TESTING_daemon_start (sched,
322                                                            pcfg,
323                                                            hostname,
324                                                            cb,
325                                                            cb_cls);
326       if (NULL == pg->peers[off].daemon)
327         GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 
328                     _("Could not start peer number %u!\n"),
329                     off);
330     }
331   return pg;
332 }
333
334
335 /**
336  * Shutdown all peers started in the given group.
337  * 
338  * @param pg handle to the peer group
339  */
340 void
341 GNUNET_TESTING_daemons_stop (struct GNUNET_TESTING_PeerGroup *pg)
342 {
343   unsigned int off;
344
345   for (off = 0; off < pg->total; off++)
346     {
347       /* FIXME: should we wait for our
348          continuations to be called here? This
349          would require us to take a continuation
350          as well... */
351       if (NULL != pg->peers[off].daemon)
352         GNUNET_TESTING_daemon_stop (pg->peers[off].daemon,
353                                     NULL, NULL);
354       if (NULL != pg->peers[off].cfg)
355         GNUNET_CONFIGURATION_destroy (pg->peers[off].cfg);
356     }
357   GNUNET_free (pg->peers);
358   if (NULL != pg->hosts)
359     {
360       GNUNET_free (pg->hosts[0].hostname);
361       GNUNET_free (pg->hosts);
362     }
363   GNUNET_free (pg);
364 }
365
366
367 /* end of testing_group.c */