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