more error checks
[oweals/gnunet.git] / src / core / test_core_api_preferences.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010, 2011 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 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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file core/test_core_api_preferences.c
22  * @brief testcase for core_api.c's GNUNET_CORE_change_preference
23  */
24 #include "platform.h"
25 #include "gnunet_common.h"
26 #include "gnunet_arm_service.h"
27 #include "gnunet_core_service.h"
28 #include "gnunet_getopt_lib.h"
29 #include "gnunet_os_lib.h"
30 #include "gnunet_program_lib.h"
31 #include "gnunet_scheduler_lib.h"
32 #include "gnunet_transport_service.h"
33
34 #define VERBOSE GNUNET_NO
35
36 #define START_ARM GNUNET_YES
37
38 #define MTYPE 12345
39
40 struct PeerContext
41 {
42   struct GNUNET_CONFIGURATION_Handle *cfg;
43   struct GNUNET_CORE_Handle *ch;
44   struct GNUNET_PeerIdentity id;   
45   struct GNUNET_TRANSPORT_Handle *th;
46   struct GNUNET_MessageHeader *hello;
47   int connect_status;
48 #if START_ARM
49   struct GNUNET_OS_Process *arm_proc;
50 #endif
51 };
52
53 static struct PeerContext p1;
54
55 static struct PeerContext p2;
56
57 static GNUNET_SCHEDULER_TaskIdentifier err_task;
58
59 static int ok;
60
61 #if VERBOSE
62 #define OKPP do { ok++; fprintf (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0)
63 #else
64 #define OKPP do { ok++; } while (0)
65 #endif
66
67 static struct GNUNET_CORE_InformationRequestContext *irc;
68
69 static struct GNUNET_CORE_TransmitHandle *th;
70
71 static GNUNET_SCHEDULER_TaskIdentifier irc_task;
72
73 static GNUNET_SCHEDULER_TaskIdentifier ask_task;
74
75 static unsigned int total_reserve = 5;
76
77
78
79 static void
80 terminate_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
81 {
82 #if VERBOSE
83   fprintf(stderr, "Regular shutdown\n");
84 #endif
85   GNUNET_assert (ok == 6);
86   GNUNET_assert (NULL == irc);
87   GNUNET_CORE_disconnect (p1.ch);
88   GNUNET_CORE_disconnect (p2.ch);
89   GNUNET_TRANSPORT_disconnect (p1.th);
90   GNUNET_TRANSPORT_disconnect (p2.th);
91   ok = 0;
92 }
93
94
95 static void
96 terminate_task_error (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
97 {
98   err_task = GNUNET_SCHEDULER_NO_TASK;
99 #if VERBOSE
100   fprintf(stderr, "ENDING ANGRILY %u\n", ok);
101 #endif
102   GNUNET_break (0);
103   if (NULL != irc)
104     {
105       GNUNET_CORE_peer_change_preference_cancel (irc);
106       irc = NULL;
107     }
108   if (GNUNET_SCHEDULER_NO_TASK != irc_task)
109     {
110       GNUNET_SCHEDULER_cancel (irc_task);
111       irc_task = GNUNET_SCHEDULER_NO_TASK;
112     }
113   if (GNUNET_SCHEDULER_NO_TASK != ask_task)
114     {
115       GNUNET_SCHEDULER_cancel (ask_task);
116       ask_task = GNUNET_SCHEDULER_NO_TASK;
117     }
118   GNUNET_CORE_disconnect (p1.ch);
119   GNUNET_CORE_disconnect (p2.ch);
120   GNUNET_TRANSPORT_disconnect (p1.th);
121   GNUNET_TRANSPORT_disconnect (p2.th);
122   ok = 42;
123 }
124
125
126 static size_t
127 transmit_ready (void *cls, size_t size, void *buf)
128 {
129   struct PeerContext *p = cls;
130   struct GNUNET_MessageHeader *m;
131
132   th = NULL;
133   GNUNET_assert (ok == 4);
134   OKPP;
135   GNUNET_assert (p == &p1);
136   GNUNET_assert (buf != NULL);
137   m = (struct GNUNET_MessageHeader *) buf;
138   m->type = htons (MTYPE);
139   m->size = htons (sizeof (struct GNUNET_MessageHeader));
140   err_task = 
141     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 120), 
142                                   &terminate_task_error, NULL);
143
144   return sizeof (struct GNUNET_MessageHeader);
145 }
146
147 static void
148 preference_cb (void *cls,
149                const struct
150                GNUNET_PeerIdentity *peer,
151                struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
152                int32_t amount,
153                struct GNUNET_TIME_Relative res_delay,
154                uint64_t preference);
155
156 static void
157 do_reserve (void *cls,
158             const struct GNUNET_SCHEDULER_TaskContext *tc)
159 {
160   struct PeerContext *pc = cls;
161
162   irc_task = GNUNET_SCHEDULER_NO_TASK;
163   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
164     {
165       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
166                   "Aborted during attempt to request reservation\n");
167       return;
168     }
169   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
170               "Requesting reservatin of 32k from core in 1s!\n");
171   irc = GNUNET_CORE_peer_change_preference (p1.ch,
172                                             &p2.id,
173                                             GNUNET_TIME_UNIT_SECONDS,
174                                             GNUNET_BANDWIDTH_VALUE_MAX,
175                                             1000000 /* bandwidth for 1s */,
176                                             0,
177                                             &preference_cb,
178                                             pc);
179 }
180
181 static void
182 preference_cb (void *cls,
183                const struct
184                GNUNET_PeerIdentity *peer,
185                struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
186                int32_t amount,
187                struct GNUNET_TIME_Relative res_delay,
188                uint64_t preference)
189 {
190   struct PeerContext *pc = cls;
191
192   irc = NULL;
193   if (0 == amount)
194     {
195       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
196                   "Delaying reservation request by %llu ms!\n",
197                   (unsigned long long) res_delay.rel_value);
198       irc_task = GNUNET_SCHEDULER_add_delayed (res_delay,
199                                                &do_reserve, pc);
200       return;
201     }
202   total_reserve--;
203   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
204               "Reservation done!\n");
205   if (total_reserve > 0)
206     {
207       irc_task = GNUNET_SCHEDULER_add_now (&do_reserve, pc);
208       return;
209     }
210   GNUNET_SCHEDULER_cancel (err_task);
211   err_task = GNUNET_SCHEDULER_NO_TASK;
212   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
213               "Asking core (1) for transmission to peer `%4s'\n",
214               GNUNET_i2s (&p2.id));
215   if (NULL == (th = GNUNET_CORE_notify_transmit_ready (p1.ch,
216                                                        GNUNET_YES,
217                                                        0,
218                                                        GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 45),
219                                                        &p2.id,
220                                                        sizeof (struct GNUNET_MessageHeader),
221                                                        &transmit_ready, &p1)))
222     {
223       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
224                   "RECEIVED NULL when asking core (1) for transmission to peer `%4s'\n",
225                   GNUNET_i2s (&p2.id));
226     }    
227 }
228
229
230 static void
231 connect_notify (void *cls,
232                 const struct GNUNET_PeerIdentity *peer,
233                 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
234 {
235   struct PeerContext *pc = cls;
236
237   if (0 == memcmp (&pc->id,
238                    peer,
239                    sizeof (struct GNUNET_PeerIdentity)))
240     return;
241   GNUNET_assert (pc->connect_status == 0);
242   pc->connect_status = 1;
243   if (pc == &p1)
244     {
245       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
246                   "Encrypted connection established to peer `%4s'\n",
247                   GNUNET_i2s (peer));
248       if (GNUNET_SCHEDULER_NO_TASK != ask_task)
249         {
250           GNUNET_SCHEDULER_cancel (ask_task);
251           ask_task = GNUNET_SCHEDULER_NO_TASK;
252         }
253       GNUNET_SCHEDULER_cancel (err_task);
254       err_task = 
255         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 120), 
256                                       &terminate_task_error, NULL);
257       irc_task = GNUNET_SCHEDULER_add_now (&do_reserve, pc);
258     }
259 }
260
261
262 static void
263 disconnect_notify (void *cls,
264                    const struct GNUNET_PeerIdentity *peer)
265 {
266   struct PeerContext *pc = cls;
267
268   if (0 == memcmp (&pc->id,
269                    peer,
270                    sizeof (struct GNUNET_PeerIdentity)))
271     return;
272   pc->connect_status = 0;
273   if (GNUNET_SCHEDULER_NO_TASK != irc_task)
274     {
275       GNUNET_SCHEDULER_cancel (irc_task);
276       irc_task = GNUNET_SCHEDULER_NO_TASK;
277     }
278   if (0 == memcmp (peer, &p1.id, sizeof (struct GNUNET_PeerIdentity)))
279     {
280       if (irc != NULL) 
281         {
282           GNUNET_CORE_peer_change_preference_cancel (irc);
283           irc = NULL;
284         }
285       if (th != NULL)
286         {
287           GNUNET_CORE_notify_transmit_ready_cancel (th);
288           th = NULL;
289         }
290     }
291   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
292               "Encrypted connection to `%4s' cut\n", 
293               GNUNET_i2s (peer));
294 }
295
296
297 static int
298 inbound_notify (void *cls,
299                 const struct GNUNET_PeerIdentity *other,
300                 const struct GNUNET_MessageHeader *message,
301                 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
302 {
303   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
304               "Core provides inbound data from `%4s'.\n", GNUNET_i2s (other));
305   return GNUNET_OK;
306 }
307
308
309 static int
310 outbound_notify (void *cls,
311                  const struct GNUNET_PeerIdentity *other,
312                  const struct GNUNET_MessageHeader *message,
313                  const struct GNUNET_TRANSPORT_ATS_Information *atsi)
314 {
315   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
316               "Core notifies about outbound data for `%4s'.\n",
317               GNUNET_i2s (other));
318   return GNUNET_OK;
319 }
320
321
322
323 static int
324 process_mtype (void *cls,
325                const struct GNUNET_PeerIdentity *peer,
326                const struct GNUNET_MessageHeader *message,
327                const struct GNUNET_TRANSPORT_ATS_Information *atsi)
328 {
329   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
330               "Receiving message from `%4s'.\n", GNUNET_i2s (peer));
331   GNUNET_assert (ok == 5);
332   OKPP;
333   GNUNET_SCHEDULER_cancel (err_task);
334   err_task = GNUNET_SCHEDULER_NO_TASK;
335   GNUNET_SCHEDULER_add_now (&terminate_task, NULL);
336   return GNUNET_OK;
337 }
338
339
340 static struct GNUNET_CORE_MessageHandler handlers[] = {
341   {&process_mtype, MTYPE, sizeof (struct GNUNET_MessageHeader)},
342   {NULL, 0, 0}
343 };
344
345
346
347 static void 
348 ask_connect_task (void *cls,
349                   const struct GNUNET_SCHEDULER_TaskContext *tc)
350 {
351   ask_task =
352     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
353                                   &ask_connect_task, NULL);
354   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
355               "Asking core (1) AGAIN to connect to peer `%4s'\n",
356               GNUNET_i2s (&p2.id));
357   GNUNET_CORE_peer_request_connect (p1.ch,
358                                     &p2.id,
359                                     NULL, NULL);
360 }
361
362
363 static void
364 init_notify (void *cls,
365              struct GNUNET_CORE_Handle *server,
366              const struct GNUNET_PeerIdentity *my_identity,
367              const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
368 {
369   struct PeerContext *p = cls;
370
371   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
372               "Core connection to `%4s' established\n",
373               GNUNET_i2s (my_identity));
374   GNUNET_assert (server != NULL);
375   p->id = *my_identity;
376   p->ch = server;
377   if (cls == &p1)
378     {
379       GNUNET_assert (ok == 2);
380       OKPP;
381       /* connect p2 */
382       GNUNET_CORE_connect (p2.cfg, 1,
383                            &p2,
384                            &init_notify,                         
385                            &connect_notify,
386                            &disconnect_notify,
387                            NULL,
388                            &inbound_notify,
389                            GNUNET_YES,
390                            &outbound_notify, GNUNET_YES, handlers);
391     }
392   else
393     {
394       GNUNET_assert (ok == 3);
395       OKPP;
396       GNUNET_assert (cls == &p2);
397       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
398                   "Asking core (1) to connect to peer `%4s'\n",
399                   GNUNET_i2s (&p2.id));
400       err_task = 
401         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 60), 
402                                       &terminate_task_error, NULL);
403       if (GNUNET_SCHEDULER_NO_TASK != ask_task)
404         GNUNET_SCHEDULER_cancel (ask_task);
405       ask_task =
406         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
407                                       &ask_connect_task, NULL);
408       GNUNET_CORE_peer_request_connect (p1.ch,
409                                         &p2.id,
410                                         NULL, NULL);
411     }
412 }
413
414
415 static void
416 process_hello (void *cls,
417                const struct GNUNET_MessageHeader *message)
418 {
419   struct PeerContext *p = cls;
420
421   GNUNET_TRANSPORT_get_hello_cancel (p->th, &process_hello, p);
422   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
423               "Received (my) `%s' from transport service\n",
424               "HELLO");
425   GNUNET_assert (message != NULL);
426   p->hello = GNUNET_malloc (ntohs (message->size));
427   memcpy (p->hello, message, ntohs (message->size));
428   if ((p == &p1) && (p2.th != NULL))
429     GNUNET_TRANSPORT_offer_hello (p2.th, message, NULL, NULL);
430   if ((p == &p2) && (p1.th != NULL))
431     GNUNET_TRANSPORT_offer_hello (p1.th, message, NULL, NULL);
432
433   if ((p == &p1) && (p2.hello != NULL))
434     GNUNET_TRANSPORT_offer_hello (p1.th, p2.hello, NULL, NULL);
435   if ((p == &p2) && (p1.hello != NULL))
436     GNUNET_TRANSPORT_offer_hello (p2.th, p1.hello, NULL, NULL);
437 }
438
439
440
441 static void
442 setup_peer (struct PeerContext *p, const char *cfgname)
443 {
444   p->cfg = GNUNET_CONFIGURATION_create ();
445 #if START_ARM
446   p->arm_proc = GNUNET_OS_start_process (NULL, NULL, "gnunet-service-arm",
447                                         "gnunet-service-arm",
448 #if VERBOSE
449                                         "-L", "DEBUG",
450 #endif
451                                         "-c", cfgname, NULL);
452 #endif
453   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
454   p->th = GNUNET_TRANSPORT_connect (p->cfg, NULL, p, NULL, NULL, NULL);
455   GNUNET_assert (p->th != NULL);
456   GNUNET_TRANSPORT_get_hello (p->th, &process_hello, p);
457 }
458
459
460 static void
461 run (void *cls,
462      char *const *args,
463      const char *cfgfile,
464      const struct GNUNET_CONFIGURATION_Handle *cfg)
465 {
466   GNUNET_assert (ok == 1);
467   OKPP;
468   setup_peer (&p1, "test_core_quota_asymmetric_recv_limited_peer1.conf");
469   setup_peer (&p2, "test_core_quota_asymmetric_recv_limited_peer2.conf");
470   GNUNET_CORE_connect (p1.cfg, 1,
471                        &p1,
472                        &init_notify,
473                        &connect_notify,
474                        &disconnect_notify,
475                        NULL,
476                        &inbound_notify,
477                        GNUNET_YES, &outbound_notify, GNUNET_YES, handlers);
478 }
479
480 static void
481 stop_arm (struct PeerContext *p)
482 {
483 #if START_ARM
484   if (0 != GNUNET_OS_process_kill (p->arm_proc, SIGTERM))
485     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
486   if (GNUNET_OS_process_wait(p->arm_proc) != GNUNET_OK)
487     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
488   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
489               "ARM process %u stopped\n", GNUNET_OS_process_get_pid (p->arm_proc));
490   GNUNET_OS_process_close (p->arm_proc);
491   p->arm_proc = NULL;
492 #endif
493   GNUNET_CONFIGURATION_destroy (p->cfg);
494 }
495
496 static int
497 check ()
498 {
499   char *const argv[] = { "test-core-api-preferences",
500     "-c",
501     "test_core_api_data.conf",
502 #if VERBOSE
503     "-L", "DEBUG",
504 #endif
505     NULL
506   };
507   struct GNUNET_GETOPT_CommandLineOption options[] = {
508     GNUNET_GETOPT_OPTION_END
509   };
510   ok = 1;
511   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
512                       argv, "test-core-api-preferences", "nohelp", options, &run, &ok);
513   stop_arm (&p1);
514   stop_arm (&p2);
515   return ok;
516 }
517
518 int
519 main (int argc, char *argv[])
520 {
521   int ret;
522
523   GNUNET_log_setup ("test-core-api",
524 #if VERBOSE
525                     "DEBUG",
526 #else
527                     "WARNING",
528 #endif
529                     NULL);
530   ret = check ();
531   GNUNET_DISK_directory_remove ("/tmp/test-gnunet-core-peer-1"); 
532   GNUNET_DISK_directory_remove ("/tmp/test-gnunet-core-peer-2");
533
534   return ret;
535 }
536
537 /* end of test_core_api_preferences.c */