-add tests
[oweals/gnunet.git] / src / ats / test_ats_lib.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2010-2015 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 ats/test_ats_lib.c
22  * @brief test ATS library with a generic interpreter for running ATS tests
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet_util_lib.h"
27 #include "gnunet_ats_service.h"
28 #include "gnunet_testing_lib.h"
29 #include "test_ats_lib.h"
30
31 /**
32  * Information about the last address suggestion we got for a peer.
33  */
34 struct AddressSuggestData
35 {
36   /**
37    * Which session were we given?
38    */
39   struct GNUNET_ATS_Session *session;
40
41   /**
42    * What address was assigned?
43    */
44   struct GNUNET_HELLO_Address *address;
45
46   /**
47    * Outbound bandwidth assigned.
48    */
49   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
50
51   /**
52    * Inbound bandwidth assigned.
53    */
54   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
55
56   /**
57    * Was the bandwidth assigned non-zero?
58    */
59   int active;
60 };
61
62
63 /**
64  * Information about the last address information we got for an address.
65  */
66 struct AddressInformationData
67 {
68   /**
69    * What address is this data about?
70    */
71   struct GNUNET_HELLO_Address *address;
72
73   /**
74    * Which properties were given?
75    */
76   struct GNUNET_ATS_Properties properties;
77
78   /**
79    * Outbound bandwidth reported.
80    */
81   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
82
83   /**
84    * Inbound bandwidth reported.
85    */
86   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
87
88   /**
89    * Was the address said to be 'active'?
90    */
91   int active;
92 };
93
94
95 /**
96  * Scheduling handle
97  */
98 static struct GNUNET_ATS_SchedulingHandle *sched_ats;
99
100 /**
101  * Connectivity handle
102  */
103 static struct GNUNET_ATS_ConnectivityHandle *con_ats;
104
105 /**
106  * Performance handle
107  */
108 static struct GNUNET_ATS_PerformanceHandle *perf_ats;
109
110 /**
111  * Handle for the interpreter task.
112  */
113 static struct GNUNET_SCHEDULER_Task *interpreter_task;
114
115 /**
116  * Map from peer identities to the last address suggestion
117  * `struct AddressSuggestData` we got for the respective peer.
118  */
119 static struct GNUNET_CONTAINER_MultiPeerMap *p2asd;
120
121 /**
122  * Map from peer identities to the last address information
123  * sets for all addresses of this peer. Each peer is mapped
124  * to one or more `struct AddressInformationData` entries.
125  */
126 static struct GNUNET_CONTAINER_MultiPeerMap *p2aid;
127
128 /**
129  * Global timeout for the test.
130  */
131 static struct GNUNET_TIME_Relative TIMEOUT;
132
133 /**
134  * Return value from #main().
135  */
136 static int ret;
137
138 /**
139  * Current global command offset into the #commands array.
140  */
141 static unsigned int off;
142
143 /**
144  * Commands for the current test.
145  */
146 static struct Command *test_commands;
147
148
149
150 /**
151  * Free `struct AddressSuggestData` entry.
152  *
153  * @param cls NULL
154  * @param key ignored
155  * @param value the `struct AddressSuggestData` to release
156  * @return #GNUNET_OK (continue to iterate)
157  */
158 static int
159 free_asd (void *cls,
160           const struct GNUNET_PeerIdentity *key,
161           void *value)
162 {
163   struct AddressSuggestData *asd = value;
164
165   GNUNET_assert (GNUNET_YES ==
166                  GNUNET_CONTAINER_multipeermap_remove (p2asd,
167                                                        key,
168                                                        asd));
169   GNUNET_free_non_null (asd->address);
170   GNUNET_free (asd);
171   return GNUNET_OK;
172 }
173
174
175 /**
176  * Free `struct AddressInformationData` entry.
177  *
178  * @param cls NULL
179  * @param key ignored
180  * @param value the `struct AddressSuggestData` to release
181  * @return #GNUNET_OK (continue to iterate)
182  */
183 static int
184 free_aid (void *cls,
185           const struct GNUNET_PeerIdentity *key,
186           void *value)
187 {
188   struct AddressInformationData *aid = value;
189
190   GNUNET_assert (GNUNET_YES ==
191                  GNUNET_CONTAINER_multipeermap_remove (p2aid,
192                                                        key,
193                                                        aid));
194   GNUNET_free (aid->address);
195   GNUNET_free (aid);
196   return GNUNET_OK;
197 }
198
199
200 /**
201  * Find latest address suggestion made for the given peer.
202  *
203  * @param pid peer to look up
204  * @return NULL if peer was never involved
205  */
206 static struct AddressSuggestData *
207 find_address_suggestion (const struct GNUNET_PeerIdentity *pid)
208 {
209   return GNUNET_CONTAINER_multipeermap_get (p2asd,
210                                             pid);
211 }
212
213
214 /**
215  * Closure for #match_address()
216  */
217 struct MatchAddressContext
218 {
219   /**
220    * Address to find.
221    */
222   const struct GNUNET_HELLO_Address *addr;
223
224   /**
225    * Where to return address information if found.
226    */
227   struct AddressInformationData *ret;
228 };
229
230
231 /**
232  * Find matching address information.
233  *
234  * @param cls a `struct MatchAddressContext`
235  * @param key unused
236  * @param value a `struct AddressInformationData`
237  * @return #GNUNET_OK if not found
238  */
239 static int
240 match_address (void *cls,
241                const struct GNUNET_PeerIdentity *key,
242                void *value)
243 {
244   struct MatchAddressContext *mac = cls;
245   struct AddressInformationData *aid = value;
246
247   if (0 == GNUNET_HELLO_address_cmp (mac->addr,
248                                      aid->address))
249   {
250     mac->ret = aid;
251     return GNUNET_NO;
252   }
253   return GNUNET_OK;
254 }
255
256
257 /**
258  * Find latest address information made for the given address.
259  *
260  * @param addr address to look up
261  * @return NULL if peer was never involved
262  */
263 static struct AddressInformationData *
264 find_address_information (const struct GNUNET_HELLO_Address *addr)
265 {
266   struct MatchAddressContext mac;
267
268   mac.ret = NULL;
269   mac.addr = addr;
270   GNUNET_CONTAINER_multipeermap_get_multiple (p2aid,
271                                               &addr->peer,
272                                               &match_address,
273                                               &mac);
274   return mac.ret;
275 }
276
277
278 /**
279  * Task run to terminate the testcase.
280  *
281  * @param cls NULL
282  */
283 static void
284 end (void *cls)
285 {
286   if (0 != ret)
287     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
288                 "Test failed at stage %u %s\n",
289                 off,
290                 (NULL != test_commands[off].label)
291                 ? test_commands[off].label
292                 : "");
293   if (NULL != interpreter_task)
294   {
295     GNUNET_SCHEDULER_cancel (interpreter_task);
296     interpreter_task = NULL;
297   }
298   if (NULL != sched_ats)
299   {
300     GNUNET_ATS_scheduling_done (sched_ats);
301     sched_ats = NULL;
302   }
303   if (NULL != con_ats)
304   {
305     GNUNET_ATS_connectivity_done (con_ats);
306     con_ats = NULL;
307   }
308   if (NULL != perf_ats)
309   {
310     GNUNET_ATS_performance_done (perf_ats);
311     perf_ats = NULL;
312   }
313   if (NULL != p2asd)
314   {
315     GNUNET_CONTAINER_multipeermap_iterate (p2asd,
316                                            &free_asd,
317                                            NULL);
318     GNUNET_CONTAINER_multipeermap_destroy (p2asd);
319     p2asd = NULL;
320   }
321   if (NULL != p2aid)
322   {
323     GNUNET_CONTAINER_multipeermap_iterate (p2aid,
324                                            &free_aid,
325                                            NULL);
326     GNUNET_CONTAINER_multipeermap_destroy (p2aid);
327     p2aid = NULL;
328   }
329 }
330
331
332 /**
333  * Main interpreter loop. Runs the steps of the test.
334  *
335  * @param cls NULL
336  */
337 static void
338 interpreter (void *cls);
339
340
341 /**
342  * Run the interpreter next.
343  */
344 static void
345 run_interpreter ()
346 {
347   if (NULL != interpreter_task)
348     GNUNET_SCHEDULER_cancel (interpreter_task);
349   interpreter_task = GNUNET_SCHEDULER_add_now (&interpreter,
350                                                NULL);
351 }
352
353
354 /**
355  * Initialize public key of a peer based on a single number.
356  *
357  * @param pid number to use as the basis
358  * @param pk resulting fake public key
359  */
360 static void
361 make_peer (uint32_t pid,
362            struct GNUNET_PeerIdentity *pk)
363 {
364   memset (pk,
365           (int) pid,
366           sizeof (struct GNUNET_PeerIdentity));
367   GNUNET_memcpy (pk,
368           &pid,
369           sizeof (uint32_t));
370 }
371
372
373 /**
374  * Generate a fake address based on the given parameters.
375  *
376  * @param pid number of the peer
377  * @param num number of the address at peer @a pid
378  * @param addr_flags flags to use for the address
379  * @return the address
380  */
381 static struct GNUNET_HELLO_Address *
382 make_address (uint32_t pid,
383               uint32_t num,
384               enum GNUNET_HELLO_AddressInfo addr_flags)
385 {
386   struct GNUNET_PeerIdentity pk;
387   uint32_t nbo;
388
389   nbo = htonl (num);
390   make_peer (pid,
391              &pk);
392   return GNUNET_HELLO_address_allocate (&pk,
393                                         "test",
394                                         &nbo,
395                                         sizeof (nbo),
396                                         addr_flags);
397 }
398
399
400 /**
401  * Our dummy sessions.
402  */
403 struct GNUNET_ATS_Session
404 {
405   /**
406    * Field to avoid `0 == sizeof(struct GNUNET_ATS_Session)`.
407    */
408   unsigned int non_empty;
409 };
410
411
412 /**
413  * Create a session instance for ATS.
414  *
415  * @param i which session number to return
416  * @return NULL if @a i is 0, otherwise a pointer unique to @a i
417  */
418 static struct GNUNET_ATS_Session *
419 make_session (unsigned int i)
420 {
421   struct GNUNET_ATS_Session *baseptr = NULL;
422
423   if (0 == i)
424     return NULL;
425   /* Yes, these are *intentionally* out-of-bounds,
426      and offset from NULL, as nobody should ever
427      use those other than to compare pointers! */
428   return baseptr + i;
429 }
430
431
432 /**
433  * Find a @a code command before the global #off with the
434  * specified @a label.
435  *
436  * @param code opcode to look for
437  * @param label label to look for, NULL for none
438  * @return previous command with the matching label
439  */
440 static struct Command *
441 find_command (enum CommandCode code,
442               const char *label)
443 {
444   int i;
445
446   if (NULL == label)
447     return NULL;
448   for (i=off-1;i>=0;i--)
449     if ( (code == test_commands[i].code) &&
450          (0 == strcmp (test_commands[i].label,
451                        label)) )
452       return &test_commands[i];
453   GNUNET_break (0);
454   return NULL;
455 }
456
457
458 /**
459  * Function called from #GNUNET_ATS_performance_list_addresses when
460  * we process a #CMD_LIST_ADDRESSES command.
461  *
462  * @param cls the `struct Command` that caused the call
463  * @param address the address, NULL if ATS service was disconnected
464  * @param address_active #GNUNET_YES if this address is actively used
465  *        to maintain a connection to a peer;
466  *        #GNUNET_NO if the address is not actively used;
467  *        #GNUNET_SYSERR if this address is no longer available for ATS
468  * @param bandwidth_out assigned outbound bandwidth for the connection
469  * @param bandwidth_in assigned inbound bandwidth for the connection
470  * @param prop performance data for the address
471  */
472 static void
473 info_cb (void *cls,
474          const struct GNUNET_HELLO_Address *address,
475          int address_active,
476          struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
477          struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
478          const struct GNUNET_ATS_Properties *prop)
479 {
480   struct Command *c = cls;
481   struct CommandListAddresses *cmd = &c->details.list_addresses;
482
483   if (NULL == address)
484   {
485     cmd->alh = NULL;
486     /* we are done with the iteration, continue to execute */
487     if ( (cmd->calls < cmd->min_calls) &&
488          (cmd->active_calls < cmd->min_active_calls) )
489     {
490       GNUNET_SCHEDULER_shutdown ();
491       return;
492     }
493     off++;
494     run_interpreter ();
495     return;
496   }
497   switch (address_active)
498   {
499   case GNUNET_YES:
500     cmd->active_calls++;
501     cmd->calls++;
502     break;
503   case GNUNET_NO:
504     cmd->calls++;
505     break;
506   case GNUNET_SYSERR:
507     return;
508   }
509   if ( (cmd->calls > cmd->max_calls) &&
510        (cmd->active_calls < cmd->max_active_calls) )
511   {
512     GNUNET_break (0);
513     GNUNET_ATS_performance_list_addresses_cancel (cmd->alh);
514     cmd->alh = NULL;
515     GNUNET_SCHEDULER_shutdown ();
516     return;
517   }
518 }
519
520
521 /**
522  * Function called with reservation result.
523  *
524  * @param cls closure with the reservation command (`struct Command`)
525  * @param peer identifies the peer
526  * @param amount set to the amount that was actually reserved or unreserved;
527  *               either the full requested amount or zero (no partial reservations)
528  * @param res_delay if the reservation could not be satisfied (amount was 0), how
529  *        long should the client wait until re-trying?
530  */
531 static void
532 reservation_cb (void *cls,
533                 const struct GNUNET_PeerIdentity *peer,
534                 int32_t amount,
535                 struct GNUNET_TIME_Relative res_delay)
536 {
537   struct Command *cmd = cls;
538   struct GNUNET_PeerIdentity pid;
539
540   cmd->details.reserve_bandwidth.rc = NULL;
541   make_peer (cmd->details.reserve_bandwidth.pid,
542              &pid);
543   GNUNET_assert (0 == memcmp (peer,
544                               &pid,
545                               sizeof (struct GNUNET_PeerIdentity)));
546   switch (cmd->details.reserve_bandwidth.expected_result)
547   {
548   case GNUNET_OK:
549     if (amount != cmd->details.reserve_bandwidth.amount)
550     {
551       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
552                   "Unexpectedly failed to reserve %d/%d bytes with delay %s!\n",
553                   (int) amount,
554                   (int) cmd->details.reserve_bandwidth.amount,
555                   GNUNET_STRINGS_relative_time_to_string (res_delay,
556                                                           GNUNET_YES));
557       GNUNET_break (0);
558       GNUNET_SCHEDULER_shutdown ();
559       return;
560     }
561     break;
562   case GNUNET_NO:
563     GNUNET_break ( (0 != amount) ||
564                    (0 != res_delay.rel_value_us) );
565     break;
566   case GNUNET_SYSERR:
567     if ( (amount != 0) ||
568          (0 == res_delay.rel_value_us) )
569     {
570       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
571                   "Unexpectedly reserved %d bytes with delay %s!\n",
572                   (int) amount,
573                   GNUNET_STRINGS_relative_time_to_string (res_delay,
574                                                           GNUNET_YES));
575       GNUNET_break (0);
576       GNUNET_SCHEDULER_shutdown ();
577       return;
578     }
579     break;
580   }
581   off++;
582   run_interpreter ();
583 }
584
585
586 /**
587  * Main interpreter loop. Runs the steps of the test.
588  *
589  * @param cls NULL
590  */
591 static void
592 interpreter (void *cls)
593
594 {
595   struct Command *cmd;
596
597   interpreter_task = NULL;
598   while (1)
599   {
600     cmd = &test_commands[off];
601     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
602                 "#%u: %d %s\n",
603                 off,
604                 (int) cmd->code,
605                 (NULL != cmd->label) ? cmd->label : "");
606     switch (cmd->code)
607     {
608     case CMD_END_PASS:
609       ret = 0;
610       GNUNET_SCHEDULER_shutdown ();
611       return;
612     case CMD_ADD_ADDRESS:
613       {
614         struct GNUNET_HELLO_Address *addr;
615         struct GNUNET_ATS_Session *session;
616
617         addr = make_address (cmd->details.add_address.pid,
618                              cmd->details.add_address.addr_num,
619                              cmd->details.add_address.addr_flags);
620         session = make_session (cmd->details.add_address.session);
621         if (cmd->details.add_address.expect_fail)
622           GNUNET_log_skip (1, GNUNET_NO);
623         cmd->details.add_address.ar
624           = GNUNET_ATS_address_add (sched_ats,
625                                     addr,
626                                     session,
627                                     &cmd->details.add_address.properties);
628         GNUNET_free (addr);
629         if (cmd->details.add_address.expect_fail)
630         {
631           GNUNET_log_skip (0, GNUNET_YES);
632         }
633         else if (NULL == cmd->details.add_address.ar)
634         {
635           GNUNET_break (0);
636           GNUNET_SCHEDULER_shutdown ();
637           return;
638         }
639         off++;
640         break;
641       }
642     case CMD_DEL_ADDRESS:
643       {
644         struct Command *add;
645
646         add = find_command (CMD_ADD_ADDRESS,
647                             cmd->details.del_address.add_label);
648         GNUNET_assert (NULL != add->details.add_address.ar);
649         GNUNET_ATS_address_destroy (add->details.add_address.ar);
650         add->details.add_address.ar = NULL;
651         off++;
652         break;
653       }
654     case CMD_AWAIT_ADDRESS_SUGGESTION:
655       {
656         struct GNUNET_PeerIdentity pid;
657         struct GNUNET_HELLO_Address *addr;
658         struct Command *add;
659         struct AddressSuggestData *asd;
660         int done;
661
662         make_peer (cmd->details.await_address_suggestion.pid,
663                    &pid);
664         asd = find_address_suggestion (&pid);
665         if (NULL == asd)
666           return;
667         if (GNUNET_NO == asd->active)
668           return; /* last suggestion was to disconnect, wait longer */
669         done = GNUNET_YES;
670         if (NULL != cmd->details.await_address_suggestion.add_label)
671         {
672           done = GNUNET_NO;
673           add = find_command (CMD_ADD_ADDRESS,
674                               cmd->details.await_address_suggestion.add_label);
675           addr = make_address (add->details.add_address.pid,
676                                add->details.add_address.addr_num,
677                                add->details.add_address.addr_flags);
678           if ( (asd->session ==
679                 make_session (add->details.add_address.session)) &&
680                (0 ==
681                 GNUNET_HELLO_address_cmp (addr,
682                                           asd->address)) )
683             done = GNUNET_YES;
684           GNUNET_free (addr);
685         }
686         if (GNUNET_NO == done)
687           return;
688         off++;
689         break;
690       }
691     case CMD_AWAIT_DISCONNECT_SUGGESTION:
692       {
693         struct GNUNET_PeerIdentity pid;
694         struct AddressSuggestData *asd;
695
696         make_peer (cmd->details.await_disconnect_suggestion.pid,
697                    &pid);
698         asd = find_address_suggestion (&pid);
699         if (NULL == asd)
700           return; /* odd, no suggestion at all yet!? */
701         if (GNUNET_YES == asd->active)
702           return; /* last suggestion was to activate, wait longer */
703         /* last suggestion was to deactivate, condition satisfied! */
704         off++;
705         break;
706       }
707     case CMD_REQUEST_CONNECTION_START:
708       {
709         struct GNUNET_PeerIdentity pid;
710
711         make_peer (cmd->details.request_connection_start.pid,
712                    &pid);
713         cmd->details.request_connection_start.csh
714           = GNUNET_ATS_connectivity_suggest (con_ats,
715                                              &pid,
716                                              1);
717         off++;
718         break;
719       }
720     case CMD_REQUEST_CONNECTION_STOP:
721       {
722         struct Command *start;
723
724         start = find_command (CMD_REQUEST_CONNECTION_START,
725                               cmd->details.request_connection_stop.connect_label);
726         GNUNET_ATS_connectivity_suggest_cancel (start->details.request_connection_start.csh);
727         start->details.request_connection_start.csh = NULL;
728         off++;
729         break;
730       }
731     case CMD_AWAIT_ADDRESS_INFORMATION:
732       {
733         struct AddressInformationData *aid;
734         struct Command *add;
735         struct Command *update;
736         struct GNUNET_HELLO_Address *addr;
737         const struct GNUNET_ATS_Properties *cmp;
738
739         add = find_command (CMD_ADD_ADDRESS,
740                             cmd->details.await_address_information.add_label);
741         update = find_command (CMD_UPDATE_ADDRESS,
742                                cmd->details.await_address_information.update_label);
743         addr = make_address (add->details.add_address.pid,
744                              add->details.add_address.addr_num,
745                              add->details.add_address.addr_flags);
746         aid = find_address_information (addr);
747         GNUNET_free (addr);
748         if (NULL == update)
749           cmp = &add->details.add_address.properties;
750         else
751           cmp = &update->details.update_address.properties;
752         if ( (NULL != aid) &&
753              (cmp->delay.rel_value_us == aid->properties.delay.rel_value_us) &&
754              (cmp->utilization_out == aid->properties.utilization_out) &&
755              (cmp->utilization_in == aid->properties.utilization_in) &&
756              (cmp->distance == aid->properties.distance) &&
757              (cmp->scope == aid->properties.scope) )
758         {
759           off++;
760           break;
761         }
762         return;
763       }
764     case CMD_UPDATE_ADDRESS:
765       {
766         struct Command *add;
767
768         add = find_command (CMD_ADD_ADDRESS,
769                             cmd->details.update_address.add_label);
770         GNUNET_assert (NULL != add->details.add_address.ar);
771         GNUNET_ATS_address_update (add->details.add_address.ar,
772                                    &cmd->details.update_address.properties);
773         off++;
774         break;
775       }
776     case CMD_ADD_SESSION:
777       {
778         struct Command *add;
779         struct GNUNET_ATS_Session *session;
780
781         add = find_command (CMD_ADD_ADDRESS,
782                             cmd->details.add_session.add_label);
783         session = make_session (cmd->details.add_session.session);
784         GNUNET_assert (NULL != add->details.add_address.ar);
785         GNUNET_ATS_address_add_session (add->details.add_address.ar,
786                                         session);
787         off++;
788         break;
789       }
790     case CMD_DEL_SESSION:
791       {
792         struct Command *add_address;
793         struct Command *add_session;
794         struct GNUNET_ATS_Session *session;
795
796         add_session = find_command (CMD_ADD_SESSION,
797                                     cmd->details.del_session.add_session_label);
798         add_address = find_command (CMD_ADD_ADDRESS,
799                                     add_session->details.add_session.add_label);
800         GNUNET_assert (NULL != add_address->details.add_address.ar);
801         session = make_session (add_session->details.add_session.session);
802         GNUNET_ATS_address_del_session (add_address->details.add_address.ar,
803                                         session);
804         off++;
805         break;
806       }
807     case CMD_CHANGE_PREFERENCE:
808       {
809         struct GNUNET_PeerIdentity pid;
810
811         make_peer (cmd->details.change_preference.pid,
812                    &pid);
813         GNUNET_ATS_performance_change_preference (perf_ats,
814                                                   &pid,
815                                                   GNUNET_ATS_PREFERENCE_END);
816         off++;
817         break;
818       }
819     case CMD_PROVIDE_FEEDBACK:
820       {
821         struct GNUNET_PeerIdentity pid;
822
823         make_peer (cmd->details.provide_feedback.pid,
824                    &pid);
825         GNUNET_ATS_performance_give_feedback (perf_ats,
826                                               &pid,
827                                               cmd->details.provide_feedback.scope,
828                                               GNUNET_ATS_PREFERENCE_END);
829         off++;
830         break;
831       }
832     case CMD_LIST_ADDRESSES:
833       {
834         struct GNUNET_PeerIdentity pid;
835
836         make_peer (cmd->details.list_addresses.pid,
837                    &pid);
838         cmd->details.list_addresses.alh
839           = GNUNET_ATS_performance_list_addresses (perf_ats,
840                                                    &pid,
841                                                    cmd->details.list_addresses.all,
842                                                    &info_cb,
843                                                    cmd);
844         return;
845       }
846     case CMD_RESERVE_BANDWIDTH:
847       {
848         struct GNUNET_PeerIdentity pid;
849
850         make_peer (cmd->details.reserve_bandwidth.pid,
851                    &pid);
852         cmd->details.reserve_bandwidth.rc
853           = GNUNET_ATS_reserve_bandwidth (perf_ats,
854                                           &pid,
855                                           cmd->details.reserve_bandwidth.amount,
856                                           &reservation_cb,
857                                           cmd);
858         return;
859       }
860     case CMD_SLEEP:
861       off++;
862       interpreter_task = GNUNET_SCHEDULER_add_delayed (cmd->details.sleep.delay,
863                                                        &interpreter,
864                                                        NULL);
865       return;
866     } /* end switch */
867   } /* end while(1) */
868 }
869
870
871 /**
872  * Signature of a function called by ATS with the current bandwidth
873  * and address preferences as determined by ATS.
874  *
875  * @param cls closure, should point to "asc-closure"
876  * @param peer for which we suggest an address, NULL if ATS connection died
877  * @param address suggested address (including peer identity of the peer),
878  *             may be NULL to signal disconnect from peer
879  * @param session session to use, NULL to establish a new outgoing session
880  * @param bandwidth_out assigned outbound bandwidth for the connection,
881  *        0 to signal disconnect
882  * @param bandwidth_in assigned inbound bandwidth for the connection,
883  *        0 to signal disconnect
884  */
885 static void
886 address_suggest_cb (void *cls,
887                     const struct GNUNET_PeerIdentity *peer,
888                     const struct GNUNET_HELLO_Address *address,
889                     struct GNUNET_ATS_Session *session,
890                     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
891                     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
892 {
893   const char *asc_cls = cls;
894   struct AddressSuggestData *asd;
895
896   GNUNET_break (0 == strcmp (asc_cls, "asc-closure"));
897   if (NULL == peer)
898   {
899     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
900                 "Connection to ATS died, likely a crash!\n");
901     GNUNET_SCHEDULER_shutdown ();
902 #if 0
903     /* This is what we should do if we wanted to continue past
904        the ATS crash. */
905     GNUNET_CONTAINER_multipeermap_iterate (p2asd,
906                                            &free_asd,
907                                            NULL);
908     GNUNET_CONTAINER_multipeermap_iterate (p2aid,
909                                            &free_aid,
910                                            NULL);
911 #endif
912     return;
913   }
914
915   asd = find_address_suggestion (peer);
916   if (NULL == asd)
917   {
918     asd = GNUNET_new (struct AddressSuggestData);
919     GNUNET_assert (GNUNET_YES ==
920                    GNUNET_CONTAINER_multipeermap_put (p2asd,
921                                                       peer,
922                                                       asd,
923                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
924   }
925   if ( (0 == ntohl (bandwidth_out.value__)) &&
926        (0 == ntohl (bandwidth_in.value__)) )
927     asd->active = GNUNET_NO;
928   else
929     asd->active = GNUNET_YES;
930   asd->bandwidth_out = bandwidth_out;
931   asd->bandwidth_in = bandwidth_in;
932   asd->session = session;
933   GNUNET_free_non_null (asd->address);
934   asd->address = NULL;
935   if (NULL != address)
936     asd->address = GNUNET_HELLO_address_copy (address);
937   if (NULL == interpreter_task)
938     run_interpreter ();
939 }
940
941
942 /**
943  * Signature of a function that is called with QoS information about an address.
944  *
945  * @param cls closure, should point to "aic-closure"
946  * @param address the address, NULL if ATS service was disconnected
947  * @param address_active #GNUNET_YES if this address is actively used
948  *        to maintain a connection to a peer;
949  *        #GNUNET_NO if the address is not actively used;
950  *        #GNUNET_SYSERR if this address is no longer available for ATS
951  * @param bandwidth_out assigned outbound bandwidth for the connection
952  * @param bandwidth_in assigned inbound bandwidth for the connection
953  * @param prop performance data for the address
954  */
955 static void
956 address_information_cb (void *cls,
957                         const struct GNUNET_HELLO_Address *address,
958                         int address_active,
959                         struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
960                         struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
961                         const struct GNUNET_ATS_Properties *prop)
962 {
963   const char *aic_cls = cls;
964   struct AddressInformationData *aid;
965
966   GNUNET_break (0 == strcmp (aic_cls, "aic-closure"));
967   if (NULL == address)
968   {
969     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
970                 "Connection to ATS died, likely a crash!\n");
971     GNUNET_CONTAINER_multipeermap_iterate (p2aid,
972                                            &free_aid,
973                                            NULL);
974     return;
975   }
976
977   aid = find_address_information (address);
978   if (NULL == aid)
979   {
980     aid = GNUNET_new (struct AddressInformationData);
981     aid->address = GNUNET_HELLO_address_copy (address);
982     GNUNET_assert (GNUNET_YES ==
983                    GNUNET_CONTAINER_multipeermap_put (p2aid,
984                                                       &address->peer,
985                                                       aid,
986                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
987   }
988   aid->active = address_active;
989   aid->bandwidth_out = bandwidth_out;
990   aid->bandwidth_in = bandwidth_in;
991   aid->properties = *prop;
992   if (NULL == interpreter_task)
993     run_interpreter ();
994 }
995
996
997 /**
998  * Function run once the ATS service has been started.
999  *
1000  * @param cls NULL
1001  * @param cfg configuration for the testcase
1002  * @param peer handle to the peer
1003  */
1004 static void
1005 run (void *cls,
1006      const struct GNUNET_CONFIGURATION_Handle *cfg,
1007      struct GNUNET_TESTING_Peer *peer)
1008 {
1009   p2asd = GNUNET_CONTAINER_multipeermap_create (128,
1010                                                 GNUNET_NO);
1011   p2aid = GNUNET_CONTAINER_multipeermap_create (128,
1012                                                 GNUNET_NO);
1013   GNUNET_SCHEDULER_add_delayed (TIMEOUT,
1014                                 &end,
1015                                 NULL);
1016
1017   sched_ats = GNUNET_ATS_scheduling_init (cfg,
1018                                           &address_suggest_cb,
1019                                           "asc-closure");
1020   if (NULL == sched_ats)
1021   {
1022     GNUNET_break (0);
1023     GNUNET_SCHEDULER_shutdown ();
1024     return;
1025   }
1026   con_ats = GNUNET_ATS_connectivity_init (cfg);
1027   if (NULL == con_ats)
1028   {
1029     GNUNET_break (0);
1030     GNUNET_SCHEDULER_shutdown ();
1031     return;
1032   }
1033   perf_ats = GNUNET_ATS_performance_init (cfg,
1034                                           &address_information_cb,
1035                                           "aic-closure");
1036   if (NULL == perf_ats)
1037   {
1038     GNUNET_break (0);
1039     GNUNET_SCHEDULER_shutdown ();
1040     return;
1041   }
1042   run_interpreter ();
1043 }
1044
1045
1046 /**
1047  * Run ATS test.
1048  *
1049  * @param argc length of @a argv
1050  * @param argv command line
1051  * @param cmds commands to run with the interpreter
1052  * @param timeout how long is the test allowed to take?
1053  * @return 0 on success
1054  */
1055 int
1056 TEST_ATS_run (int argc,
1057               char *argv[],
1058               struct Command *cmds,
1059               struct GNUNET_TIME_Relative timeout)
1060 {
1061   char *test_filename = GNUNET_strdup (argv[0]);
1062   char *sep;
1063   char *config_file;
1064   char *underscore;
1065
1066   test_commands = cmds;
1067   TIMEOUT = timeout;
1068   if (NULL != (sep = strstr (test_filename, ".exe")))
1069     sep[0] = '\0';
1070   underscore = strrchr (test_filename, (int) '_');
1071   GNUNET_assert (NULL != underscore);
1072   GNUNET_asprintf (&config_file,
1073                    "test_ats_api_%s.conf",
1074                    underscore + 1);
1075   ret = 2;
1076   if (0 != GNUNET_TESTING_peer_run ("test-ats-api",
1077                                     config_file,
1078                                     &run, NULL))
1079     ret = 1;
1080   GNUNET_free (test_filename);
1081   GNUNET_free (config_file);
1082   return ret;
1083 }
1084
1085 /* end of test_ats_lib.c */