removing duplicate specifiers
[oweals/gnunet.git] / src / testbed / test_testbed_api_3peers_3controllers.c
1 /*
2       This file is part of GNUnet
3       (C) 2008--2012 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 /**
22  * @file testbed/test_testbed_api_3peers_3controllers.c
23  * @brief testcases for the testbed api: 3 peers are configured, started and
24  *          connected together. Each peer resides on its own controller.
25  * @author Sree Harsha Totakura
26  */
27
28
29 /**
30  * The testing architecture is:
31  *                  C1
32  *                 /  \
33  *                /    \
34  *               C2 == C3
35  * C1 is the master controller and C2, C3 are slave controllers. C2 links to C3
36  * laterally
37  */
38
39 #include "platform.h"
40 #include "gnunet_util_lib.h"
41 #include "gnunet_testing_lib-new.h"
42 #include "gnunet_testbed_service.h"
43
44
45 /**
46  * Generic logging shortcut
47  */
48 #define LOG(kind,...)                           \
49   GNUNET_log (kind, __VA_ARGS__)
50
51 /**
52  * Relative time seconds shorthand
53  */
54 #define TIME_REL_SECS(sec) \
55   GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, sec)
56
57
58 /**
59  * Peer context
60  */
61 struct PeerContext
62 {
63   /**
64    * The peer handle
65    */
66   struct GNUNET_TESTBED_Peer *peer;
67
68   /**
69    * Operations involving this peer
70    */
71   struct GNUNET_TESTBED_Operation *operation;
72
73   /**
74    * set to GNUNET_YES when peer is started
75    */
76   int is_running;
77 };
78
79 /**
80  * Our localhost
81  */
82 static struct GNUNET_TESTBED_Host *host;
83
84 /**
85  * The controller process of one controller
86  */
87 static struct GNUNET_TESTBED_ControllerProc *cp1;
88
89 /**
90  * A neighbouring host
91  */
92 static struct GNUNET_TESTBED_Host *neighbour1;
93
94 /**
95  * Another neighbouring host
96  */
97 static struct GNUNET_TESTBED_Host *neighbour2;
98
99 /**
100  * Handle for neighbour registration
101  */
102 static struct GNUNET_TESTBED_HostRegistrationHandle *reg_handle;
103
104 /**
105  * The controller handle of one controller
106  */
107 static struct GNUNET_TESTBED_Controller *controller1;
108
109 /**
110  * The controller handle of another controller
111  */
112 static struct GNUNET_TESTBED_Controller *controller2;
113
114 /**
115  * peer 1
116  */
117 static struct PeerContext peer1;
118
119 /**
120  * peer2
121  */
122 static struct PeerContext peer2;
123
124 /**
125  * peer3
126  */
127 static struct PeerContext peer3;
128
129 /**
130  * Handle to starting configuration
131  */
132 static struct GNUNET_CONFIGURATION_Handle *cfg;
133
134 /**
135  * Handle to controller2 configuration, used to establish lateral link from
136  * controller 1
137  */
138 static struct GNUNET_CONFIGURATION_Handle *cfg2;
139
140 /**
141  * Handle to operations involving both peers
142  */
143 static struct GNUNET_TESTBED_Operation *common_operation;
144
145 /**
146  * Abort task identifier
147  */
148 static GNUNET_SCHEDULER_TaskIdentifier abort_task;
149
150 /**
151  * Delayed connect job identifier
152  */
153 static GNUNET_SCHEDULER_TaskIdentifier delayed_connect_task;
154
155 /**
156  * Different stages in testing
157  */
158 enum Stage
159 {
160
161   /**
162    * Initial stage
163    */
164   INIT,
165
166   /**
167    * Controller 1 has started
168    */
169   CONTROLLER1_UP,
170
171   /**
172    * peer1 is created
173    */
174   PEER1_CREATED,
175
176   /**
177    * peer1 is started
178    */
179   PEER1_STARTED,
180
181   /**
182    * Controller 2 has started
183    */
184   CONTROLLER2_UP,
185
186   /**
187    * peer2 is created
188    */
189   PEER2_CREATED,
190
191   /**
192    * peer2 is started
193    */
194   PEER2_STARTED,
195
196   /**
197    * Controller 3 has started
198    */
199   CONTROLLER3_UP,
200
201   /**
202    * Peer3 is created
203    */
204   PEER3_CREATED,
205
206   /**
207    * Peer3 started
208    */
209   PEER3_STARTED,
210
211   /**
212    * peers are connected
213    */
214   PEERS_CONNECTED,
215
216   /**
217    * Peers are connected once again (this should not fail as they are already connected)
218    */
219   PEERS_CONNECTED_2,
220
221   /**
222    * peers are stopped
223    */
224   PEERS_STOPPED,
225
226   /**
227    * Final success stage
228    */
229   SUCCESS
230 };
231
232 /**
233  * The testing result
234  */
235 static enum Stage result;
236
237 /**
238  * Shutdown nicely
239  *
240  * @param cls NULL
241  * @param tc the task context
242  */
243 static void
244 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
245 {
246   if (GNUNET_SCHEDULER_NO_TASK != abort_task)
247     GNUNET_SCHEDULER_cancel (abort_task);
248   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == delayed_connect_task);
249   if (NULL != reg_handle)
250     GNUNET_TESTBED_cancel_registration (reg_handle);
251   if (NULL != controller1)
252     GNUNET_TESTBED_controller_disconnect (controller1);
253   if (NULL != controller2)
254     GNUNET_TESTBED_controller_disconnect (controller2);
255   GNUNET_CONFIGURATION_destroy (cfg);
256   if (NULL != cfg2)
257     GNUNET_CONFIGURATION_destroy (cfg2);
258   if (NULL != cp1)
259     GNUNET_TESTBED_controller_stop (cp1);
260   if (NULL != host)
261     GNUNET_TESTBED_host_destroy (host);
262   if (NULL != neighbour1)
263     GNUNET_TESTBED_host_destroy (neighbour1);
264   if (NULL != neighbour2)
265     GNUNET_TESTBED_host_destroy (neighbour2);
266 }
267
268
269 /**
270  * abort task to run on test timed out
271  *
272  * @param cls NULL
273  * @param tc the task context
274  */
275 static void
276 do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
277 {
278   LOG (GNUNET_ERROR_TYPE_WARNING, "Test timedout -- Aborting\n");
279   abort_task = GNUNET_SCHEDULER_NO_TASK;
280   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == delayed_connect_task);
281   do_shutdown (cls, tc);
282 }
283
284
285 /**
286  * Callback to be called when an operation is completed
287  *
288  * @param cls the callback closure from functions generating an operation
289  * @param op the operation that has been finished
290  * @param emsg error message in case the operation has failed; will be NULL if
291  *          operation has executed successfully.
292  */
293 static void 
294 op_comp_cb (void *cls, struct GNUNET_TESTBED_Operation *op, const char *emsg);
295
296
297 /**
298  * task for delaying a connect
299  *
300  * @param cls NULL
301  * @param tc the task context
302  */
303 static void
304 do_delayed_connect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
305 {
306   delayed_connect_task = GNUNET_SCHEDULER_NO_TASK;
307   GNUNET_assert (NULL == common_operation);
308   common_operation = GNUNET_TESTBED_overlay_connect (NULL, &op_comp_cb, NULL, 
309                                                      peer1.peer, peer2.peer);
310 }
311
312
313 /**
314  * Callback to be called when an operation is completed
315  *
316  * @param cls the callback closure from functions generating an operation
317  * @param op the operation that has been finished
318  * @param emsg error message in case the operation has failed; will be NULL if
319  *          operation has executed successfully.
320  */
321 static void 
322 op_comp_cb (void *cls, struct GNUNET_TESTBED_Operation *op, const char *emsg)
323 {
324   GNUNET_assert (common_operation == op);
325   switch(result)
326   {
327   case PEER3_STARTED:
328     GNUNET_assert (NULL == peer1.operation);
329     GNUNET_assert (NULL == peer2.operation);
330     GNUNET_assert (NULL != common_operation);
331     GNUNET_TESTBED_operation_done (common_operation);
332     common_operation = NULL;
333     result = PEERS_CONNECTED;
334     LOG (GNUNET_ERROR_TYPE_DEBUG, "Peers connected\n");
335     delayed_connect_task =
336         GNUNET_SCHEDULER_add_delayed (TIME_REL_SECS (3),
337                                       &do_delayed_connect, NULL);
338     break;
339   case PEERS_CONNECTED:
340     GNUNET_assert (NULL == peer1.operation);
341     GNUNET_assert (NULL == peer2.operation);
342     GNUNET_assert (NULL != common_operation);
343     GNUNET_TESTBED_operation_done (common_operation);
344     common_operation = NULL;
345     result = PEERS_CONNECTED_2;
346     LOG (GNUNET_ERROR_TYPE_DEBUG, "Peers connected again\n");
347     peer1.operation = GNUNET_TESTBED_peer_stop (peer1.peer, NULL, NULL);
348     peer2.operation = GNUNET_TESTBED_peer_stop (peer2.peer, NULL, NULL);
349     peer3.operation = GNUNET_TESTBED_peer_stop (peer3.peer, NULL, NULL);
350     break;
351   default:
352     GNUNET_assert (0);
353   }
354 }
355
356
357 /**
358  * Functions of this signature are called when a peer has been successfully
359  * created
360  *
361  * @param cls NULL
362  * @param peer the handle for the created peer; NULL on any error during
363  *          creation
364  * @param emsg NULL if peer is not NULL; else MAY contain the error description
365  */
366 static void
367 peer_create_cb (void *cls, struct GNUNET_TESTBED_Peer *peer, const char *emsg)
368 {
369   switch (result)
370   {
371   case CONTROLLER1_UP:
372     GNUNET_assert (NULL != peer1.operation);
373     GNUNET_assert (NULL != peer);
374     GNUNET_assert (NULL == peer1.peer);
375     peer1.peer = peer;
376     GNUNET_TESTBED_operation_done (peer1.operation);
377     result = PEER1_CREATED;
378     peer1.operation = GNUNET_TESTBED_peer_start (peer, NULL, NULL);
379     break;
380   case CONTROLLER2_UP:
381     GNUNET_assert (NULL != peer2.operation);
382     GNUNET_assert (NULL != peer);
383     GNUNET_assert (NULL == peer2.peer);
384     peer2.peer = peer;
385     GNUNET_TESTBED_operation_done (peer2.operation);
386     result = PEER2_CREATED;
387     peer2.operation = GNUNET_TESTBED_peer_start (peer, NULL, NULL);
388     break;
389   case CONTROLLER3_UP:
390     GNUNET_assert (NULL != peer3.operation);
391     GNUNET_assert (NULL != peer);
392     GNUNET_assert (NULL == peer3.peer);
393     peer3.peer = peer;
394     GNUNET_TESTBED_operation_done (peer3.operation);
395     result = PEER3_CREATED;
396     peer3.operation = GNUNET_TESTBED_peer_start (peer, NULL, NULL);
397     break;
398   default:
399     GNUNET_assert (0);
400   }  
401 }
402
403
404 /**
405  * Callback to signal successfull startup of the controller process
406  *
407  * @param cls the closure from GNUNET_TESTBED_controller_start()
408  * @param cfg the configuration with which the controller has been started;
409  *          NULL if status is not GNUNET_OK
410  * @param status GNUNET_OK if the startup is successfull; GNUNET_SYSERR if not,
411  *          GNUNET_TESTBED_controller_stop() shouldn't be called in this case
412  */
413 static void
414 status_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *config, int status);
415
416
417 /**
418  * Signature of the event handler function called by the
419  * respective event controller.
420  *
421  * @param cls closure
422  * @param event information about the event
423  */
424 static void
425 controller_cb (void *cls, const struct GNUNET_TESTBED_EventInformation *event)
426 {
427   switch (event->type)
428   {
429   case GNUNET_TESTBED_ET_OPERATION_FINISHED:
430     GNUNET_assert (NULL == event->details.operation_finished.op_cls);
431     GNUNET_assert (NULL == event->details.operation_finished.emsg);
432     GNUNET_assert (NULL == event->details.operation_finished.generic);
433     switch (result)
434     {
435     case PEERS_STOPPED:
436       if (event->details.operation_finished.operation == peer1.operation)
437       {
438         GNUNET_TESTBED_operation_done (peer1.operation);
439         peer1.operation = NULL;
440         peer1.peer = NULL;
441       }
442       else if (event->details.operation_finished.operation == peer2.operation)
443       {
444         GNUNET_TESTBED_operation_done (peer2.operation);
445         peer2.operation = NULL;
446         peer2.peer = NULL;
447       }
448       else if (event->details.operation_finished.operation == peer3.operation)
449       {
450         GNUNET_TESTBED_operation_done (peer3.operation);
451         peer3.operation = NULL;
452         peer3.peer = NULL;
453       }
454       else
455         GNUNET_assert (0);
456       if ((NULL == peer1.peer) && (NULL == peer2.peer) && (NULL == peer3.peer))
457       {
458         result = SUCCESS;
459         GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
460       }
461       break;
462     case PEER1_STARTED:
463       GNUNET_assert (NULL != common_operation);
464       GNUNET_TESTBED_operation_done (common_operation);
465       common_operation = NULL;
466       result = CONTROLLER2_UP;
467       peer2.operation =
468           GNUNET_TESTBED_peer_create (controller1, neighbour1, cfg,
469                                       &peer_create_cb, NULL);
470       GNUNET_assert (NULL != peer2.operation);
471       break;
472     case PEER2_STARTED:
473       GNUNET_assert (NULL != common_operation);
474       GNUNET_TESTBED_operation_done (common_operation);
475       common_operation = NULL;
476       result = CONTROLLER3_UP;
477       peer3.operation =
478           GNUNET_TESTBED_peer_create (controller1, neighbour2, cfg,
479                                       &peer_create_cb, NULL);
480       GNUNET_assert (NULL != peer3.operation);
481       break;
482     default:
483       GNUNET_assert (0);
484     }
485     break;
486   case GNUNET_TESTBED_ET_PEER_START:    
487     switch (result)
488     {
489     case PEER1_CREATED:
490       GNUNET_assert (event->details.peer_start.host == host);
491       peer1.is_running = GNUNET_YES;
492       GNUNET_TESTBED_operation_done (peer1.operation);
493       peer1.operation = NULL;
494       result = PEER1_STARTED;
495       common_operation =
496           GNUNET_TESTBED_controller_link (controller1, neighbour1, NULL, cfg,
497                                           GNUNET_YES); 
498       break;
499     case PEER2_CREATED:
500       GNUNET_assert (event->details.peer_start.host == neighbour1);
501       peer2.is_running = GNUNET_YES;
502       GNUNET_TESTBED_operation_done (peer2.operation);
503       peer2.operation = NULL;
504       result = PEER2_STARTED;
505       GNUNET_assert (NULL == common_operation);              
506       common_operation =
507           GNUNET_TESTBED_controller_link (controller1, neighbour2, NULL, cfg,
508                                           GNUNET_YES);
509       GNUNET_assert (NULL != common_operation);
510       break;
511     case PEER3_CREATED:
512       GNUNET_assert (event->details.peer_start.host == neighbour2);
513       peer3.is_running = GNUNET_YES;
514       GNUNET_TESTBED_operation_done (peer3.operation);
515       peer3.operation = NULL;
516       result = PEER3_STARTED;
517       common_operation =
518           GNUNET_TESTBED_overlay_connect (NULL, &op_comp_cb, NULL, peer1.peer,
519                                           peer2.peer);
520       break;
521     default:
522       GNUNET_assert (0);
523     }    
524     break;
525   case GNUNET_TESTBED_ET_PEER_STOP:
526     GNUNET_assert (PEERS_CONNECTED_2 == result);
527     if (event->details.peer_stop.peer == peer1.peer)
528     {
529       peer1.is_running = GNUNET_NO;
530       GNUNET_TESTBED_operation_done (peer1.operation);
531     }
532     else if (event->details.peer_stop.peer == peer2.peer)
533     {
534       peer2.is_running = GNUNET_NO;
535       GNUNET_TESTBED_operation_done (peer2.operation);
536     }
537     else if (event->details.peer_stop.peer == peer3.peer)
538     {
539       peer3.is_running = GNUNET_NO;
540       GNUNET_TESTBED_operation_done (peer3.operation);
541     }
542     else
543       GNUNET_assert (0);
544     if ((GNUNET_NO == peer1.is_running) &&
545         (GNUNET_NO == peer2.is_running) &&
546         (GNUNET_NO == peer3.is_running))
547     {
548       result = PEERS_STOPPED;
549       peer1.operation = GNUNET_TESTBED_peer_destroy (peer1.peer);
550       peer2.operation = GNUNET_TESTBED_peer_destroy (peer2.peer);
551       peer3.operation = GNUNET_TESTBED_peer_destroy (peer3.peer);
552     }
553     break;
554   case GNUNET_TESTBED_ET_CONNECT:
555     switch (result)
556     {
557     case PEER3_STARTED:
558     case PEERS_CONNECTED:
559       GNUNET_assert (NULL == peer1.operation);
560       GNUNET_assert (NULL == peer2.operation);
561       GNUNET_assert (NULL == peer3.operation);
562       GNUNET_assert (NULL != common_operation);
563       /* GNUNET_assert ((event->details.peer_connect.peer1 == peer1.peer) && */
564       /*             (event->details.peer_connect.peer2 == peer2.peer)); */
565       break;
566     default:
567       GNUNET_assert (0);
568     }
569     break;
570   default:
571     GNUNET_assert (0);
572   };
573 }
574
575
576 /**
577  * Callback which will be called to after a host registration succeeded or failed
578  *
579  * @param cls the host which has been registered
580  * @param emsg the error message; NULL if host registration is successful
581  */
582 static void
583 registration_comp (void *cls, const char *emsg)
584 {
585   reg_handle = NULL;
586   if (cls == neighbour1)
587   {
588     neighbour2 = GNUNET_TESTBED_host_create ("127.0.0.1", NULL, 0);
589     GNUNET_assert (NULL != neighbour2);
590     reg_handle =
591         GNUNET_TESTBED_register_host (controller1, neighbour2, &registration_comp,
592                                       neighbour2);
593     GNUNET_assert (NULL != reg_handle);
594     return;
595   }
596   GNUNET_assert (cls == neighbour2);
597   peer1.operation =
598       GNUNET_TESTBED_peer_create (controller1, host, cfg, &peer_create_cb,
599                                   &peer1);
600   GNUNET_assert (NULL != peer1.operation);  
601 }
602
603
604 /**
605  * Callback to signal successfull startup of the controller process
606  *
607  * @param cls the closure from GNUNET_TESTBED_controller_start()
608  * @param cfg the configuration with which the controller has been started;
609  *          NULL if status is not GNUNET_OK
610  * @param status GNUNET_OK if the startup is successfull; GNUNET_SYSERR if not,
611  *          GNUNET_TESTBED_controller_stop() shouldn't be called in this case
612  */
613 static void
614 status_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *config, int status)
615 {
616   uint64_t event_mask;
617   
618   GNUNET_assert (GNUNET_OK == status);
619   event_mask = 0;
620   event_mask |= (1L << GNUNET_TESTBED_ET_PEER_START);
621   event_mask |= (1L << GNUNET_TESTBED_ET_PEER_STOP);
622   event_mask |= (1L << GNUNET_TESTBED_ET_CONNECT);
623   event_mask |= (1L << GNUNET_TESTBED_ET_OPERATION_FINISHED);
624   switch (result)
625   {
626   case INIT:
627     controller1 =
628         GNUNET_TESTBED_controller_connect (config, host, event_mask, &controller_cb,
629                                            NULL);
630     GNUNET_assert (NULL != controller1);
631     result = CONTROLLER1_UP;
632     neighbour1 = GNUNET_TESTBED_host_create ("127.0.0.1", NULL, 0);
633     GNUNET_assert (NULL != neighbour1);
634     reg_handle =
635         GNUNET_TESTBED_register_host (controller1, neighbour1, &registration_comp,
636                                       neighbour1);
637     GNUNET_assert (NULL != reg_handle);
638     break;
639   default:
640     GNUNET_assert (0);
641   }
642   
643 }
644
645
646 /**
647  * Main run function.
648  *
649  * @param cls NULL
650  * @param args arguments passed to GNUNET_PROGRAM_run
651  * @param cfgfile the path to configuration file
652  * @param cfg the configuration file handle
653  */
654 static void
655 run (void *cls, char *const *args, const char *cfgfile,
656      const struct GNUNET_CONFIGURATION_Handle *config)
657 {
658   host = GNUNET_TESTBED_host_create (NULL, NULL, 0);
659   GNUNET_assert (NULL != host);
660   cfg = GNUNET_CONFIGURATION_dup (config);
661   cp1 = GNUNET_TESTBED_controller_start ("127.0.0.1", host, cfg, status_cb,
662                                         NULL);
663   abort_task =
664       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
665                                     (GNUNET_TIME_UNIT_MINUTES, 3), &do_abort,
666                                     NULL);
667 }
668
669
670 /**
671  * Function to check if password-less SSH logins to given ip work
672  *
673  * @param host_str numeric representation of the host's ip
674  * @return GNUNET_YES if password-less SSH login to the given host works;
675  *           GNUNET_NO if not
676  */
677 static int
678 check_ssh (char *host_str)
679 {
680   char *const remote_args[] = {
681     "ssh", "-o", "BatchMode=yes", "-o", "CheckHostIP=no", "-q",
682     host_str, "echo", "SSH", "works", NULL
683   };
684   struct GNUNET_OS_Process *auxp;
685   enum GNUNET_OS_ProcessStatusType type;
686   unsigned long code;
687   int ret;
688
689   auxp =
690       GNUNET_OS_start_process_vap (GNUNET_NO, GNUNET_OS_INHERIT_STD_ALL, NULL,
691                                    NULL, "ssh", remote_args);
692   GNUNET_assert (NULL != auxp);
693   do
694   {
695     ret = GNUNET_OS_process_status (auxp, &type, &code);
696     GNUNET_assert (GNUNET_SYSERR != ret);
697     (void) usleep (300);
698   }
699   while (GNUNET_NO == ret);
700   (void) GNUNET_OS_process_wait (auxp);
701   GNUNET_OS_process_destroy (auxp);
702   return (0 != code) ? GNUNET_NO : GNUNET_YES;
703 }
704
705
706 /**
707  * Main function
708  */
709 int
710 main (int argc, char **argv)
711 {
712   char *const argv2[] = { "test_testbed_api_3peers_3controllers",
713     "-c", "test_testbed_api.conf",
714     NULL
715   };
716   struct GNUNET_GETOPT_CommandLineOption options[] = {
717     GNUNET_GETOPT_OPTION_END
718   };
719   int ret;
720
721   //GNUNET_OS_network_interfaces_list (&interface_processor, NULL);
722
723   if (GNUNET_YES != check_ssh ("127.0.0.1"))
724     goto error_exit;
725   result = INIT;
726   ret =
727       GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1, argv2,
728                           "test_testbed_api_3peers_3controllers", "nohelp",
729                           options, &run, NULL);
730   if ((GNUNET_OK != ret) || (SUCCESS != result))
731     return 1;
732   return 0;
733
734  error_exit:
735   (void) PRINTF ("%s",
736                  "Unable to run the test as this system is not configured "
737                  "to use password less SSH logins to localhost.\n"
738                  "Marking test as successful\n");
739   return 0;
740 }
741
742 /* end of test_testbed_api_3peers_3controllers.c */