add now instead of add delayed
[oweals/gnunet.git] / src / testing / test_testing_topology_churn.c
1 /*
2      This file is part of GNUnet.
3      (C) 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  * @file testing/test_testing_topology_churn.c
22  * @brief base testcase for testing simple churn functionality
23  */
24 #include "platform.h"
25 #include "gnunet_testing_lib.h"
26 #include "gnunet_core_service.h"
27
28 #define VERBOSE GNUNET_YES
29
30 /**
31  * How long until we fail the whole testcase?
32  */
33 #define TEST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600)
34
35 /**
36  * How long until we give up on starting the peers? (Must be longer than the connect timeout!)
37  */
38 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300)
39
40 #define DEFAULT_NUM_PEERS 4
41
42 static int ok;
43
44 static unsigned long long num_peers;
45
46 static unsigned int expected_connections;
47
48 static unsigned int expected_failed_connections;
49
50 static unsigned long long peers_left;
51
52 static struct GNUNET_TESTING_PeerGroup *pg;
53
54 static struct GNUNET_SCHEDULER_Handle *sched;
55
56 const struct GNUNET_CONFIGURATION_Handle *main_cfg;
57
58 GNUNET_SCHEDULER_TaskIdentifier die_task;
59
60 static char *test_directory;
61
62 #define MTYPE 12345
63
64 struct GNUNET_TestMessage
65 {
66   /**
67    * Header of the message
68    */
69   struct GNUNET_MessageHeader header;
70
71   /**
72    * Unique identifier for this message.
73    */
74   uint32_t uid;
75 };
76
77 static void
78 finish_testing ()
79 {
80   GNUNET_assert (pg != NULL);
81
82   if (die_task != GNUNET_SCHEDULER_NO_TASK)
83     GNUNET_SCHEDULER_cancel(sched, die_task);
84
85 #if VERBOSE
86   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
87               "Called finish testing, stopping daemons.\n");
88 #endif
89
90 #if VERBOSE
91           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
92                       "Calling daemons_stop\n");
93 #endif
94   GNUNET_TESTING_daemons_stop (pg, TIMEOUT);
95 #if VERBOSE
96           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
97                       "daemons_stop finished\n");
98 #endif
99
100   ok = 0;
101 }
102
103 static void
104 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
105 {
106   char *msg = cls;
107   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
108               "End badly was called (%s)... stopping daemons.\n", msg);
109
110   if (pg != NULL)
111     {
112       GNUNET_TESTING_daemons_stop (pg, TIMEOUT);
113       ok = 7331;                /* Opposite of leet */
114     }
115   else
116     ok = 401;                   /* Never got peers started */
117
118 }
119
120 struct ChurnTestContext
121 {
122   GNUNET_SCHEDULER_Task next_task;
123
124 };
125
126 static struct ChurnTestContext churn_ctx;
127
128 /**
129  * Churn callback, report on success or failure of churn operation.
130  *
131  * @param cls closure
132  * @param emsg NULL on success
133  */
134 void churn_callback(void *cls,
135                     const char *emsg)
136 {
137   if (emsg == NULL)
138     {
139       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Successfully churned peers!\n", emsg);
140       GNUNET_SCHEDULER_add_now(sched, churn_ctx.next_task, NULL);
141     }
142   else
143     {
144       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to churn peers with error `%s'\n", emsg);
145       GNUNET_SCHEDULER_cancel(sched, die_task);
146       die_task = GNUNET_SCHEDULER_add_now(sched, &end_badly, NULL);
147     }
148 }
149
150
151 static void
152 churn_peers_both()
153 {
154   churn_ctx.next_task = &finish_testing;
155   GNUNET_TESTING_daemons_churn(pg, 1, 1, TIMEOUT, &churn_callback, NULL);
156 }
157
158 static void
159 churn_peers_off_again()
160 {
161   churn_ctx.next_task = &churn_peers_both;
162   GNUNET_TESTING_daemons_churn(pg, 2, 0, TIMEOUT, &churn_callback, NULL);
163 }
164
165 static void
166 churn_peers_on()
167 {
168   churn_ctx.next_task = &churn_peers_off_again;
169   GNUNET_TESTING_daemons_churn(pg, 0, 2, TIMEOUT, &churn_callback, NULL);
170 }
171
172 static void
173 churn_peers_off()
174 {
175   churn_ctx.next_task = &churn_peers_on;
176   GNUNET_TESTING_daemons_churn(pg, 2, 0, TIMEOUT, &churn_callback, NULL);
177 }
178
179 static void
180 peers_started_callback (void *cls,
181        const struct GNUNET_PeerIdentity *id,
182        const struct GNUNET_CONFIGURATION_Handle *cfg,
183        struct GNUNET_TESTING_Daemon *d, const char *emsg)
184 {
185   if (emsg != NULL)
186     {
187       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to start daemon with error: `%s'\n",
188                   emsg);
189       return;
190     }
191   GNUNET_assert (id != NULL);
192 #if VERBOSE
193   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Started daemon %llu out of %llu\n",
194               (num_peers - peers_left) + 1, num_peers);
195 #endif
196   peers_left--;
197   if (peers_left == 0)
198     {
199 #if VERBOSE
200       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
201                   "All %d daemons started, now testing churn!\n",
202                   num_peers);
203 #endif
204       GNUNET_SCHEDULER_cancel (sched, die_task);
205       /* Set up task in case topology creation doesn't finish
206        * within a reasonable amount of time */
207       die_task = GNUNET_SCHEDULER_add_delayed (sched,
208                                                GNUNET_TIME_relative_multiply
209                                                (GNUNET_TIME_UNIT_MINUTES, 5),
210                                                &end_badly, "from peers_started_callback");
211       churn_peers_off ();
212       ok = 0;
213     }
214 }
215
216
217 static void
218 run (void *cls,
219      struct GNUNET_SCHEDULER_Handle *s,
220      char *const *args,
221      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
222 {
223   sched = s;
224   ok = 1;
225
226 #if VERBOSE
227   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
228               "Starting daemons based on config file %s\n", cfgfile);
229 #endif
230
231   if (GNUNET_YES != GNUNET_CONFIGURATION_get_value_string(cfg, "paths", "servicehome", &test_directory))
232     {
233       ok = 404;
234       return;
235     }
236
237   if (GNUNET_SYSERR ==
238       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers",
239                                              &num_peers))
240     num_peers = DEFAULT_NUM_PEERS;
241
242   main_cfg = cfg;
243
244   peers_left = num_peers;
245
246   /* For this specific test we only really want a CLIQUE topology as the
247    * overlay allowed topology, and a RING topology as the underlying connection
248    * allowed topology.  So we will expect only num_peers * 2 connections to
249    * work, and (num_peers * (num_peers - 1)) - (num_peers * 2) to fail.
250    */
251   expected_connections = num_peers * (num_peers - 1);
252   expected_failed_connections = expected_connections - (num_peers * 2);
253
254
255   /* Set up a task to end testing if peer start fails */
256   die_task = GNUNET_SCHEDULER_add_delayed (sched,
257                                            GNUNET_TIME_relative_multiply
258                                            (GNUNET_TIME_UNIT_MINUTES, 5),
259                                            &end_badly, "didn't start all daemons in reasonable amount of time!!!");
260
261   pg = GNUNET_TESTING_daemons_start (sched, cfg,
262                                      peers_left, TIMEOUT, NULL, NULL, &peers_started_callback, NULL,
263                                      NULL, NULL, NULL);
264
265 }
266
267 static int
268 check ()
269 {
270   int ret;
271   char *const argv[] = {"test-testing-topology-churn",
272     "-c",
273     "test_testing_data_topology_churn.conf",
274 #if VERBOSE
275     "-L", "DEBUG",
276 #endif
277     NULL
278   };
279   struct GNUNET_GETOPT_CommandLineOption options[] = {
280     GNUNET_GETOPT_OPTION_END
281   };
282   ret = GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
283                       argv, "test-testing-topology-churn", "nohelp",
284                       options, &run, &ok);
285   if (ret != GNUNET_OK)
286     {
287       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "`test-testing-topology-churn': Failed with error code %d\n", ret);
288     }
289
290   return ok;
291 }
292
293 int
294 main (int argc, char *argv[])
295 {
296   int ret;
297
298   GNUNET_log_setup ("test_testing_topology_churn",
299 #if VERBOSE
300                     "DEBUG",
301 #else
302                     "WARNING",
303 #endif
304                     NULL);
305   ret = check ();
306
307   /**
308    * Need to remove base directory, subdirectories taken care
309    * of by the testing framework.
310    */
311   if (test_directory != NULL)
312     {
313       if (GNUNET_DISK_directory_remove (test_directory) != GNUNET_OK)
314         {
315           GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to remove testing directory %s\n", test_directory);
316         }
317     }
318
319   return ret;
320 }
321
322 /* end of test_testing_topology_churn.c */