new testsuite, obsoleting (most of) the old one
[oweals/gnunet.git] / src / ats / test_ats_lib.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2010-2015 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 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 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 (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  * @param tc unused
283  */
284 static void
285 end (void *cls,
286      const struct GNUNET_SCHEDULER_TaskContext *tc)
287 {
288   if (0 != ret)
289     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
290                 "Test failed at stage %u %s\n",
291                 off,
292                 (NULL != test_commands[off].label)
293                 ? test_commands[off].label
294                 : "");
295   if (NULL != interpreter_task)
296   {
297     GNUNET_SCHEDULER_cancel (interpreter_task);
298     interpreter_task = NULL;
299   }
300   if (NULL != sched_ats)
301   {
302     GNUNET_ATS_scheduling_done (sched_ats);
303     sched_ats = NULL;
304   }
305   if (NULL != con_ats)
306   {
307     GNUNET_ATS_connectivity_done (con_ats);
308     con_ats = NULL;
309   }
310   if (NULL != perf_ats)
311   {
312     GNUNET_ATS_performance_done (perf_ats);
313     perf_ats = NULL;
314   }
315   if (NULL != p2asd)
316   {
317     GNUNET_CONTAINER_multipeermap_iterate (p2asd,
318                                            &free_asd,
319                                            NULL);
320     GNUNET_CONTAINER_multipeermap_destroy (p2asd);
321     p2asd = NULL;
322   }
323   if (NULL != p2aid)
324   {
325     GNUNET_CONTAINER_multipeermap_iterate (p2aid,
326                                            &free_aid,
327                                            NULL);
328     GNUNET_CONTAINER_multipeermap_destroy (p2aid);
329     p2aid = NULL;
330   }
331 }
332
333
334 /**
335  * Main interpreter loop. Runs the steps of the test.
336  *
337  * @param cls NULL
338  * @param tc unused
339  */
340 static void
341 interpreter (void *cls,
342              const struct GNUNET_SCHEDULER_TaskContext *tc);
343
344
345 /**
346  * Run the interpreter next.
347  */
348 static void
349 run_interpreter ()
350 {
351   if (NULL != interpreter_task)
352     GNUNET_SCHEDULER_cancel (interpreter_task);
353   interpreter_task = GNUNET_SCHEDULER_add_now (&interpreter,
354                                                NULL);
355 }
356
357
358 /**
359  * Initialize public key of a peer based on a single number.
360  *
361  * @param pid number to use as the basis
362  * @param pk resulting fake public key
363  */
364 static void
365 make_peer (uint32_t pid,
366            struct GNUNET_PeerIdentity *pk)
367 {
368   memset (pk,
369           (int) pid,
370           sizeof (struct GNUNET_PeerIdentity));
371   memcpy (pk,
372           &pid,
373           sizeof (uint32_t));
374 }
375
376
377 /**
378  * Generate a fake address based on the given parameters.
379  *
380  * @param pid number of the peer
381  * @param num number of the address at peer @a pid
382  * @param addr_flags flags to use for the address
383  * @return the address
384  */
385 static struct GNUNET_HELLO_Address *
386 make_address (uint32_t pid,
387               uint32_t num,
388               enum GNUNET_HELLO_AddressInfo addr_flags)
389 {
390   struct GNUNET_PeerIdentity pk;
391   uint32_t nbo;
392
393   nbo = htonl (num);
394   make_peer (pid,
395              &pk);
396   return GNUNET_HELLO_address_allocate (&pk,
397                                         "test",
398                                         &nbo,
399                                         sizeof (nbo),
400                                         addr_flags);
401 }
402
403
404 /**
405  * Our dummy sessions.
406  */
407 struct Session {
408   /**
409    * Field to avoid `0 == sizeof(struct Session)`.
410    */
411   unsigned int non_empty;
412 };
413
414
415 /**
416  * Create a session instance for ATS.
417  *
418  * @param i which session number to return
419  * @return NULL if @a i is 0, otherwise a pointer unique to @a i
420  */
421 static struct Session *
422 make_session (unsigned int i)
423 {
424   struct Session *baseptr = NULL;
425
426   if (0 == i)
427     return NULL;
428   /* Yes, these are *intentionally* out-of-bounds,
429      and offset from NULL, as nobody should ever
430      use those other than to compare pointers! */
431   return baseptr + i;
432 }
433
434
435 /**
436  * Find a @a code command before the global #off with the
437  * specified @a label.
438  *
439  * @param code opcode to look for
440  * @param label label to look for, NULL for none
441  * @return previous command with the matching label
442  */
443 static struct Command *
444 find_command (enum CommandCode code,
445               const char *label)
446 {
447   int i;
448
449   if (NULL == label)
450     return NULL;
451   for (i=off-1;i>=0;i--)
452     if ( (code == test_commands[i].code) &&
453          (0 == strcmp (test_commands[i].label,
454                        label)) )
455       return &test_commands[i];
456   GNUNET_break (0);
457   return NULL;
458 }
459
460
461 /**
462  * Function called from #GNUNET_ATS_performance_list_addresses when
463  * we process a #CMD_LIST_ADDRESSES command.
464  *
465  * @param cls the `struct Command` that caused the call
466  * @param address the address, NULL if ATS service was disconnected
467  * @param address_active #GNUNET_YES if this address is actively used
468  *        to maintain a connection to a peer;
469  *        #GNUNET_NO if the address is not actively used;
470  *        #GNUNET_SYSERR if this address is no longer available for ATS
471  * @param bandwidth_out assigned outbound bandwidth for the connection
472  * @param bandwidth_in assigned inbound bandwidth for the connection
473  * @param prop performance data for the address
474  */
475 static void
476 info_cb (void *cls,
477          const struct GNUNET_HELLO_Address *address,
478          int address_active,
479          struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
480          struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
481          const struct GNUNET_ATS_Properties *prop)
482 {
483   struct Command *c = cls;
484   struct CommandListAddresses *cmd = &c->details.list_addresses;
485
486   if (NULL == address)
487   {
488     cmd->alh = NULL;
489     /* we are done with the iteration, continue to execute */
490     if ( (cmd->calls < cmd->min_calls) &&
491          (cmd->active_calls < cmd->min_active_calls) )
492     {
493       GNUNET_SCHEDULER_shutdown ();
494       return;
495     }
496     off++;
497     run_interpreter ();
498     return;
499   }
500   switch (address_active)
501   {
502   case GNUNET_YES:
503     cmd->active_calls++;
504     cmd->calls++;
505     break;
506   case GNUNET_NO:
507     cmd->calls++;
508     break;
509   case GNUNET_SYSERR:
510     return;
511   }
512   if ( (cmd->calls > cmd->max_calls) &&
513        (cmd->active_calls < cmd->max_active_calls) )
514   {
515     GNUNET_break (0);
516     GNUNET_ATS_performance_list_addresses_cancel (cmd->alh);
517     cmd->alh = NULL;
518     GNUNET_SCHEDULER_shutdown ();
519     return;
520   }
521 }
522
523
524 /**
525  * Main interpreter loop. Runs the steps of the test.
526  *
527  * @param cls NULL
528  * @param tc unused
529  */
530 static void
531 interpreter (void *cls,
532              const struct GNUNET_SCHEDULER_TaskContext *tc)
533
534 {
535   struct Command *cmd;
536
537   interpreter_task = NULL;
538   while (1)
539   {
540     cmd = &test_commands[off];
541     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
542                 "#%u: %d %s\n",
543                 off,
544                 (int) cmd->code,
545                 (NULL != cmd->label) ? cmd->label : "");
546     switch (cmd->code)
547     {
548     case CMD_END_PASS:
549       ret = 0;
550       GNUNET_SCHEDULER_shutdown ();
551       return;
552     case CMD_ADD_ADDRESS:
553       {
554         struct GNUNET_HELLO_Address *addr;
555         struct Session *session;
556
557         addr = make_address (cmd->details.add_address.pid,
558                              cmd->details.add_address.addr_num,
559                              cmd->details.add_address.addr_flags);
560         session = make_session (cmd->details.add_address.session);
561         if (cmd->details.add_address.expect_fail)
562           GNUNET_log_skip (1, GNUNET_NO);
563         cmd->details.add_address.ar
564           = GNUNET_ATS_address_add (sched_ats,
565                                     addr,
566                                     session,
567                                     &cmd->details.add_address.properties);
568         GNUNET_free (addr);
569         if (cmd->details.add_address.expect_fail)
570         {
571           GNUNET_log_skip (0, GNUNET_YES);
572         }
573         else if (NULL == cmd->details.add_address.ar)
574         {
575           GNUNET_break (0);
576           GNUNET_SCHEDULER_shutdown ();
577           return;
578         }
579         off++;
580         break;
581       }
582     case CMD_DEL_ADDRESS:
583       {
584         struct Command *add;
585
586         add = find_command (CMD_ADD_ADDRESS,
587                             cmd->details.del_address.add_label);
588         GNUNET_assert (NULL != add->details.add_address.ar);
589         GNUNET_ATS_address_destroy (add->details.add_address.ar);
590         add->details.add_address.ar = NULL;
591         off++;
592         break;
593       }
594     case CMD_AWAIT_ADDRESS_SUGGESTION:
595       {
596         struct GNUNET_PeerIdentity pid;
597         struct GNUNET_HELLO_Address *addr;
598         struct Command *add;
599         struct AddressSuggestData *asd;
600         int done;
601
602         make_peer (cmd->details.await_address_suggestion.pid,
603                    &pid);
604         asd = find_address_suggestion (&pid);
605         if (NULL == asd)
606           return;
607         if (GNUNET_NO == asd->active)
608           return;
609         done = GNUNET_YES;
610         if (NULL != cmd->details.await_address_suggestion.add_label)
611         {
612           done = GNUNET_NO;
613           add = find_command (CMD_ADD_ADDRESS,
614                               cmd->details.await_address_suggestion.add_label);
615           addr = make_address (add->details.add_address.pid,
616                                add->details.add_address.addr_num,
617                                add->details.add_address.addr_flags);
618           if ( (asd->session ==
619                 make_session (add->details.add_address.session)) &&
620                (0 ==
621                 GNUNET_HELLO_address_cmp (addr,
622                                           asd->address)) )
623             done = GNUNET_YES;
624           GNUNET_free (addr);
625         }
626         if (GNUNET_NO == done)
627           return;
628         off++;
629         break;
630       }
631     case CMD_AWAIT_DISCONNECT_SUGGESTION:
632       {
633         struct GNUNET_PeerIdentity pid;
634         struct AddressSuggestData *asd;
635
636         make_peer (cmd->details.await_disconnect_suggestion.pid,
637                    &pid);
638         asd = find_address_suggestion (&pid);
639         if (NULL == asd)
640           return;
641         if (GNUNET_NO == asd->active)
642           return;
643         off++;
644         break;
645       }
646     case CMD_REQUEST_CONNECTION_START:
647       {
648         struct GNUNET_PeerIdentity pid;
649
650         make_peer (cmd->details.request_connection_start.pid,
651                    &pid);
652         cmd->details.request_connection_start.csh
653           = GNUNET_ATS_connectivity_suggest (con_ats,
654                                              &pid);
655         off++;
656         break;
657       }
658     case CMD_REQUEST_CONNECTION_STOP:
659       {
660         struct Command *start;
661
662         start = find_command (CMD_REQUEST_CONNECTION_START,
663                               cmd->details.request_connection_stop.connect_label);
664         GNUNET_ATS_connectivity_suggest_cancel (start->details.request_connection_start.csh);
665         start->details.request_connection_start.csh = NULL;
666         off++;
667         break;
668       }
669     case CMD_AWAIT_ADDRESS_INFORMATION:
670       {
671         struct AddressInformationData *aid;
672         struct Command *add;
673         struct Command *update;
674         struct GNUNET_HELLO_Address *addr;
675         const struct GNUNET_ATS_Properties *cmp;
676
677         add = find_command (CMD_ADD_ADDRESS,
678                             cmd->details.await_address_information.add_label);
679         update = find_command (CMD_UPDATE_ADDRESS,
680                                cmd->details.await_address_information.update_label);
681         addr = make_address (add->details.add_address.pid,
682                              add->details.add_address.addr_num,
683                              add->details.add_address.addr_flags);
684         aid = find_address_information (addr);
685         GNUNET_free (addr);
686         if (NULL == update)
687           cmp = &add->details.add_address.properties;
688         else
689           cmp = &update->details.update_address.properties;
690         if ( (NULL != aid) &&
691              (0 == memcmp (cmp,
692                            &aid->properties,
693                            sizeof (struct GNUNET_ATS_Properties))) )
694         {
695           off++;
696           break;
697         }
698         return;
699       }
700     case CMD_UPDATE_ADDRESS:
701       {
702         struct Command *add;
703
704         add = find_command (CMD_ADD_ADDRESS,
705                             cmd->details.update_address.add_label);
706         GNUNET_assert (NULL != add->details.add_address.ar);
707         GNUNET_ATS_address_update (add->details.add_address.ar,
708                                    &cmd->details.update_address.properties);
709         off++;
710         break;
711       }
712     case CMD_ADD_SESSION:
713       {
714         struct Command *add;
715         struct Session *session;
716
717         add = find_command (CMD_ADD_ADDRESS,
718                             cmd->details.add_session.add_label);
719         session = make_session (cmd->details.add_session.session);
720         GNUNET_assert (NULL != add->details.add_address.ar);
721         GNUNET_ATS_address_add_session (add->details.add_address.ar,
722                                         session);
723         off++;
724         break;
725       }
726     case CMD_DEL_SESSION:
727       {
728         struct Command *add_address;
729         struct Command *add_session;
730         struct Session *session;
731
732         add_session = find_command (CMD_ADD_SESSION,
733                                     cmd->details.del_session.add_session_label);
734         add_address = find_command (CMD_ADD_ADDRESS,
735                                     add_session->details.add_session.add_label);
736         GNUNET_assert (NULL != add_address->details.add_address.ar);
737         session = make_session (add_session->details.add_session.session);
738         GNUNET_ATS_address_del_session (add_address->details.add_address.ar,
739                                         session);
740         off++;
741         break;
742       }
743     case CMD_CHANGE_PREFERENCE:
744       {
745         struct GNUNET_PeerIdentity pid;
746
747         make_peer (cmd->details.change_preference.pid,
748                    &pid);
749         GNUNET_ATS_performance_change_preference (perf_ats,
750                                                   &pid,
751                                                   GNUNET_ATS_PREFERENCE_END);
752         off++;
753         break;
754       }
755     case CMD_PROVIDE_FEEDBACK:
756       {
757         struct GNUNET_PeerIdentity pid;
758
759         make_peer (cmd->details.provide_feedback.pid,
760                    &pid);
761         GNUNET_ATS_performance_give_feedback (perf_ats,
762                                               &pid,
763                                               cmd->details.provide_feedback.scope,
764                                               GNUNET_ATS_PREFERENCE_END);
765         off++;
766         break;
767       }
768     case CMD_LIST_ADDRESSES:
769       {
770         struct GNUNET_PeerIdentity pid;
771
772         make_peer (cmd->details.list_addresses.pid,
773                    &pid);
774         cmd->details.list_addresses.alh
775           = GNUNET_ATS_performance_list_addresses (perf_ats,
776                                                    &pid,
777                                                    cmd->details.list_addresses.all,
778                                                    &info_cb,
779                                                    cmd);
780         return;
781       }
782     } /* end switch */
783   } /* end while(1) */
784 }
785
786
787 /**
788  * Signature of a function called by ATS with the current bandwidth
789  * and address preferences as determined by ATS.
790  *
791  * @param cls closure, should point to "asc-closure"
792  * @param peer for which we suggest an address, NULL if ATS connection died
793  * @param address suggested address (including peer identity of the peer),
794  *             may be NULL to signal disconnect from peer
795  * @param session session to use, NULL to establish a new outgoing session
796  * @param bandwidth_out assigned outbound bandwidth for the connection,
797  *        0 to signal disconnect
798  * @param bandwidth_in assigned inbound bandwidth for the connection,
799  *        0 to signal disconnect
800  */
801 static void
802 address_suggest_cb (void *cls,
803                     const struct GNUNET_PeerIdentity *peer,
804                     const struct GNUNET_HELLO_Address *address,
805                     struct Session *session,
806                     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
807                     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
808 {
809   const char *asc_cls = cls;
810   struct AddressSuggestData *asd;
811
812   GNUNET_break (0 == strcmp (asc_cls, "asc-closure"));
813   if (NULL == peer)
814   {
815     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
816                 "Connection to ATS died, likely a crash!\n");
817     GNUNET_SCHEDULER_shutdown ();
818 #if 0
819     /* This is what we should do if we wanted to continue past
820        the ATS crash. */
821     GNUNET_CONTAINER_multipeermap_iterate (p2asd,
822                                            &free_asd,
823                                            NULL);
824     GNUNET_CONTAINER_multipeermap_iterate (p2aid,
825                                            &free_aid,
826                                            NULL);
827 #endif
828     return;
829   }
830
831   asd = find_address_suggestion (peer);
832   if (NULL == asd)
833   {
834     asd = GNUNET_new (struct AddressSuggestData);
835     GNUNET_assert (GNUNET_YES ==
836                    GNUNET_CONTAINER_multipeermap_put (p2asd,
837                                                       peer,
838                                                       asd,
839                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
840   }
841   if ( (0 == ntohl (bandwidth_out.value__)) &&
842        (0 == ntohl (bandwidth_in.value__)) )
843     asd->active = GNUNET_NO;
844   else
845     asd->active = GNUNET_YES;
846   asd->bandwidth_out = bandwidth_out;
847   asd->bandwidth_in = bandwidth_in;
848   asd->session = session;
849   GNUNET_free_non_null (asd->address);
850   asd->address = NULL;
851   if (NULL != address)
852     asd->address = GNUNET_HELLO_address_copy (address);
853   run_interpreter ();
854 }
855
856
857 /**
858  * Signature of a function that is called with QoS information about an address.
859  *
860  * @param cls closure, should point to "aic-closure"
861  * @param address the address, NULL if ATS service was disconnected
862  * @param address_active #GNUNET_YES if this address is actively used
863  *        to maintain a connection to a peer;
864  *        #GNUNET_NO if the address is not actively used;
865  *        #GNUNET_SYSERR if this address is no longer available for ATS
866  * @param bandwidth_out assigned outbound bandwidth for the connection
867  * @param bandwidth_in assigned inbound bandwidth for the connection
868  * @param prop performance data for the address
869  */
870 static void
871 address_information_cb (void *cls,
872                         const struct GNUNET_HELLO_Address *address,
873                         int address_active,
874                         struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
875                         struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
876                         const struct GNUNET_ATS_Properties *prop)
877 {
878   const char *aic_cls = cls;
879   struct AddressInformationData *aid;
880
881   GNUNET_break (0 == strcmp (aic_cls, "aic-closure"));
882   if (NULL == address)
883   {
884     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
885                 "Connection to ATS died, likely a crash!\n");
886     GNUNET_CONTAINER_multipeermap_iterate (p2aid,
887                                            &free_aid,
888                                            NULL);
889     return;
890   }
891
892   aid = find_address_information (address);
893   if (NULL == aid)
894   {
895     aid = GNUNET_new (struct AddressInformationData);
896     aid->address = GNUNET_HELLO_address_copy (address);
897     GNUNET_assert (GNUNET_YES ==
898                    GNUNET_CONTAINER_multipeermap_put (p2aid,
899                                                       &address->peer,
900                                                       aid,
901                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
902   }
903   aid->active = address_active;
904   aid->bandwidth_out = bandwidth_out;
905   aid->bandwidth_in = bandwidth_in;
906   aid->properties = *prop;
907   run_interpreter ();
908 }
909
910
911 /**
912  * Function run once the ATS service has been started.
913  *
914  * @param cls NULL
915  * @param cfg configuration for the testcase
916  * @param peer handle to the peer
917  */
918 static void
919 run (void *cls,
920      const struct GNUNET_CONFIGURATION_Handle *cfg,
921      struct GNUNET_TESTING_Peer *peer)
922 {
923   p2asd = GNUNET_CONTAINER_multipeermap_create (128,
924                                                 GNUNET_NO);
925   p2aid = GNUNET_CONTAINER_multipeermap_create (128,
926                                                 GNUNET_NO);
927   GNUNET_SCHEDULER_add_delayed (TIMEOUT,
928                                 &end,
929                                 NULL);
930
931   sched_ats = GNUNET_ATS_scheduling_init (cfg,
932                                           &address_suggest_cb,
933                                           "asc-closure");
934   if (NULL == sched_ats)
935   {
936     GNUNET_break (0);
937     GNUNET_SCHEDULER_shutdown ();
938     return;
939   }
940   con_ats = GNUNET_ATS_connectivity_init (cfg);
941   if (NULL == con_ats)
942   {
943     GNUNET_break (0);
944     GNUNET_SCHEDULER_shutdown ();
945     return;
946   }
947   perf_ats = GNUNET_ATS_performance_init (cfg,
948                                           &address_information_cb,
949                                           "aic-closure");
950   if (NULL == perf_ats)
951   {
952     GNUNET_break (0);
953     GNUNET_SCHEDULER_shutdown ();
954     return;
955   }
956   run_interpreter ();
957 }
958
959
960 /**
961  * Run ATS test.
962  *
963  * @param argc length of @a argv
964  * @param argv command line
965  * @param cmds commands to run with the interpreter
966  * @param timeout how long is the test allowed to take?
967  * @return 0 on success
968  */
969 int
970 TEST_ATS_run (int argc,
971               char *argv[],
972               struct Command *cmds,
973               struct GNUNET_TIME_Relative timeout)
974 {
975   char *test_filename = GNUNET_strdup (argv[0]);
976   char *sep;
977   char *config_file;
978   char *underscore;
979
980   test_commands = cmds;
981   TIMEOUT = timeout;
982   if (NULL != (sep = strstr (test_filename, ".exe")))
983     sep[0] = '\0';
984   underscore = strrchr (test_filename, (int) '_');
985   GNUNET_assert (NULL != underscore);
986   GNUNET_asprintf (&config_file,
987                    "test_ats_api_%s.conf",
988                    underscore + 1);
989   ret = 2;
990   if (0 != GNUNET_TESTING_peer_run ("test-ats-api",
991                                     config_file,
992                                     &run, NULL))
993     ret = 1;
994   GNUNET_free (test_filename);
995   GNUNET_free (config_file);
996   return ret;
997 }
998
999 /* end of test_ats_lib.c */