2 This file is part of GNUnet
3 (C) 2008, 2009, 2012 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file testing/testing.c
23 * @brief convenience API for writing testcases for GNUnet
24 * Many testcases need to start and stop a peer/service
25 * and this library is supposed to make that easier
26 * for TESTCASES. Normal programs should always
27 * use functions from gnunet_{util,arm}_lib.h. This API is
28 * ONLY for writing testcases (or internal use of the testbed).
29 * @author Christian Grothoff
33 #include "gnunet_util_lib.h"
34 #include "gnunet_testing_lib-new.h"
36 #define LOG(kind,...) \
37 GNUNET_log_from (kind, "gnunettestingnew", __VA_ARGS__)
40 * Size of a hostkey when written to a file
42 #define HOSTKEYFILESIZE 914
45 * Lowest port used for GNUnet testing. Should be high enough to not
46 * conflict with other applications running on the hosts but be low
47 * enough to not conflict with client-ports (typically starting around
50 #define LOW_PORT 12000
53 * Highest port used for GNUnet testing. Should be low enough to not
54 * conflict with the port range for "local" ports (client apps; see
55 * /proc/sys/net/ipv4/ip_local_port_range on Linux for example).
57 #define HIGH_PORT 56000
61 * Handle for a system on which GNUnet peers are executed;
62 * a system is used for reserving unique paths and ports.
64 struct GNUNET_TESTING_System
67 * Prefix (i.e. "/tmp/gnunet-testing/") we prepend to each
72 * The hostname of the controller
77 * Hostkeys data, contains "HOSTKEYFILESIZE * total_hostkeys" bytes.
82 * memory map for 'hostkeys_data'.
84 struct GNUNET_DISK_MapHandle *map;
87 * File descriptor for the map.
89 struct GNUNET_DISK_FileHandle *map_fd;
92 * Bitmap where each TCP port that has already been reserved for
93 * some GNUnet peer is recorded. Note that we additionally need to
94 * test if a port is already in use by non-GNUnet components before
95 * assigning it to a peer/service. If we detect that a port is
96 * already in use, we also mark it in this bitmap. So all the bits
97 * that are zero merely indicate ports that MIGHT be available for
100 uint32_t reserved_tcp_ports[65536 / 32];
103 * Bitmap where each UDP port that has already been reserved for
104 * some GNUnet peer is recorded. Note that we additionally need to
105 * test if a port is already in use by non-GNUnet components before
106 * assigning it to a peer/service. If we detect that a port is
107 * already in use, we also mark it in this bitmap. So all the bits
108 * that are zero merely indicate ports that MIGHT be available for
111 uint32_t reserved_udp_ports[65536 / 32];
114 * Counter we use to make service home paths unique on this system;
115 * the full path consists of the tmppath and this number. Each
116 * UNIXPATH for a peer is also modified to include the respective
117 * path counter to ensure uniqueness. This field is incremented
118 * by one for each configured peer. Even if peers are destroyed,
119 * we never re-use path counters.
121 uint32_t path_counter;
124 * The number of hostkeys
126 uint32_t total_hostkeys;
131 * Handle for a GNUnet peer controlled by testing.
133 struct GNUNET_TESTING_Peer
137 * Path to the configuration file for this peer.
142 * Binary to be executed during 'GNUNET_TESTING_peer_start'.
143 * Typically 'gnunet-service-arm' (but can be set to a
144 * specific service by 'GNUNET_TESTING_service_run' if
150 * Handle to the running binary of the service, NULL if the
151 * peer/service is currently not running.
153 struct GNUNET_OS_Process *main_process;
158 * Testing includes a number of pre-created hostkeys for faster peer
159 * startup. This function loads such keys into memory from a file.
161 * @param system the testing system handle
162 * @return GNUNET_OK on success; GNUNET_SYSERR on error
165 hostkeys_load (struct GNUNET_TESTING_System *system)
171 GNUNET_assert (NULL == system->hostkeys_data);
172 data_dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
173 GNUNET_asprintf (&filename, "%s/testing_hostkeys.dat", data_dir);
174 GNUNET_free (data_dir);
176 if (GNUNET_YES != GNUNET_DISK_file_test (filename))
178 LOG (GNUNET_ERROR_TYPE_ERROR,
179 _("Hostkeys file not found: %s\n"), filename);
180 GNUNET_free (filename);
181 return GNUNET_SYSERR;
183 /* Check hostkey file size, read entire thing into memory */
185 GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES))
189 GNUNET_free (filename);
190 return GNUNET_SYSERR; /* File is empty */
192 if (0 != (fs % HOSTKEYFILESIZE))
194 LOG (GNUNET_ERROR_TYPE_ERROR,
195 _("Incorrect hostkey file format: %s\n"), filename);
196 GNUNET_free (filename);
197 return GNUNET_SYSERR;
199 system->map_fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ,
200 GNUNET_DISK_PERM_NONE);
201 if (NULL == system->map_fd)
203 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", filename);
204 GNUNET_free (filename);
205 return GNUNET_SYSERR;
207 system->total_hostkeys = fs / HOSTKEYFILESIZE;
208 system->hostkeys_data = GNUNET_DISK_file_map (system->map_fd,
210 GNUNET_DISK_MAP_TYPE_READ,
212 GNUNET_free (filename);
218 * Function to remove the loaded hostkeys
220 * @param system the testing system handle
223 hostkeys_unload (struct GNUNET_TESTING_System *system)
225 GNUNET_break (NULL != system->hostkeys_data);
226 system->hostkeys_data = NULL;
227 GNUNET_DISK_file_unmap (system->map);
229 GNUNET_DISK_file_close (system->map_fd);
230 system->map_fd = NULL;
231 system->hostkeys_data = NULL;
232 system->total_hostkeys = 0;
237 * Create a system handle. There must only be one system
238 * handle per operating system.
240 * @param testdir only the directory name without any path. This is used for
241 * all service homes; the directory will be created in a temporary
242 * location depending on the underlying OS
244 * @param controller hostname of the controlling host,
245 * service configurations are modified to allow
246 * control connections from this host; can be NULL
247 * @return handle to this system, NULL on error
249 struct GNUNET_TESTING_System *
250 GNUNET_TESTING_system_create (const char *testdir,
251 const char *controller)
253 struct GNUNET_TESTING_System *system;
255 GNUNET_assert (NULL != testdir);
256 system = GNUNET_malloc (sizeof (struct GNUNET_TESTING_System));
257 system->tmppath = GNUNET_DISK_mkdtemp (testdir);
258 if (NULL == system->tmppath)
260 GNUNET_free (system);
263 if (NULL != controller)
264 system->controller = GNUNET_strdup (controller);
265 if (GNUNET_OK != hostkeys_load (system))
267 GNUNET_TESTING_system_destroy (system, GNUNET_YES);
275 * Free system resources.
277 * @param system system to be freed
278 * @param remove_paths should the 'testdir' and all subdirectories
279 * be removed (clean up on shutdown)?
282 GNUNET_TESTING_system_destroy (struct GNUNET_TESTING_System *system,
285 if (NULL != system->hostkeys_data)
286 hostkeys_unload (system);
287 if (GNUNET_YES == remove_paths)
288 GNUNET_DISK_directory_remove (system->tmppath);
289 GNUNET_free (system->tmppath);
290 GNUNET_free_non_null (system->controller);
291 GNUNET_free (system);
296 * Reserve a TCP or UDP port for a peer.
298 * @param system system to use for reservation tracking
299 * @param is_tcp GNUNET_YES for TCP ports, GNUNET_NO for UDP
300 * @return 0 if no free port was available
303 GNUNET_TESTING_reserve_port (struct GNUNET_TESTING_System *system,
306 struct GNUNET_NETWORK_Handle *socket;
307 struct addrinfo hint;
308 struct addrinfo *ret;
309 uint32_t *port_buckets;
318 FIXME: Instead of using getaddrinfo we should try to determine the port
319 status by the following heurestics.
321 On systems which support both IPv4 and IPv6, only ports open on both
322 address families are considered open.
323 On system with either IPv4 or IPv6. A port is considered open if it's
324 open in the respective address family
326 hint.ai_family = AF_UNSPEC; /* IPv4 and IPv6 */
327 hint.ai_socktype = (GNUNET_YES == is_tcp)? SOCK_STREAM : SOCK_DGRAM;
328 hint.ai_protocol = 0;
331 hint.ai_canonname = NULL;
333 hint.ai_flags = AI_PASSIVE | AI_NUMERICSERV; /* Wild card address */
334 port_buckets = (GNUNET_YES == is_tcp) ?
335 system->reserved_tcp_ports : system->reserved_udp_ports;
336 for (index = (LOW_PORT / 32) + 1; index < (HIGH_PORT / 32); index++)
338 xor_image = (UINT32_MAX ^ port_buckets[index]);
339 if (0 == xor_image) /* Ports in the bucket are full */
344 if (0 == ((xor_image >> pos) & 1U))
349 open_port = (index * 32) + pos;
350 GNUNET_asprintf (&open_port_str, "%u", (unsigned int) open_port);
352 GNUNET_assert (0 == getaddrinfo (NULL, open_port_str, &hint, &ret));
353 GNUNET_free (open_port_str);
354 socket = GNUNET_NETWORK_socket_create (ret->ai_family,
355 (GNUNET_YES == is_tcp) ?
356 SOCK_STREAM : SOCK_DGRAM,
358 GNUNET_assert (NULL != socket);
359 bind_status = GNUNET_NETWORK_socket_bind (socket,
363 GNUNET_NETWORK_socket_close (socket);
365 port_buckets[index] |= (1U << pos); /* Set the port bit */
366 if (GNUNET_OK == bind_status)
368 LOG (GNUNET_ERROR_TYPE_DEBUG,
369 "Found a free port %u\n", (unsigned int) open_port);
380 * Release reservation of a TCP or UDP port for a peer
381 * (used during GNUNET_TESTING_peer_destroy).
383 * @param system system to use for reservation tracking
384 * @param is_tcp GNUNET_YES for TCP ports, GNUNET_NO for UDP
385 * @param port reserved port to release
388 GNUNET_TESTING_release_port (struct GNUNET_TESTING_System *system,
392 uint32_t *port_buckets;
396 port_buckets = (GNUNET_YES == is_tcp) ?
397 system->reserved_tcp_ports : system->reserved_udp_ports;
400 LOG (GNUNET_ERROR_TYPE_DEBUG, "Releasing port %u\n", port);
401 if (0 == (port_buckets[bucket] & (1U << pos)))
403 GNUNET_break(0); /* Port was not reserved by us using reserve_port() */
406 port_buckets[bucket] &= ~(1U << pos);
411 * Reserve a SERVICEHOME path for a peer.
413 * @param system system to use for reservation tracking
414 * @return NULL on error, otherwise fresh unique path to use
415 * as the servicehome for the peer; must be freed by the caller
419 reserve_path (struct GNUNET_TESTING_System *system)
423 GNUNET_asprintf (&reserved_path,
424 "%s/%u", system->tmppath, system->path_counter++);
425 return reserved_path;
430 * Testing includes a number of pre-created hostkeys for
431 * faster peer startup. This function can be used to
432 * access the n-th key of those pre-created hostkeys; note
433 * that these keys are ONLY useful for testing and not
434 * secure as the private keys are part of the public
435 * GNUnet source code.
437 * This is primarily a helper function used internally
438 * by 'GNUNET_TESTING_peer_configure'.
440 * @param system the testing system handle
441 * @param key_number desired pre-created hostkey to obtain
442 * @param id set to the peer's identity (hash of the public
443 * key; if NULL, GNUNET_SYSERR is returned immediately
444 * @return GNUNET_SYSERR on error (not enough keys)
447 GNUNET_TESTING_hostkey_get (const struct GNUNET_TESTING_System *system,
449 struct GNUNET_PeerIdentity *id)
451 struct GNUNET_CRYPTO_RsaPrivateKey *private_key;
452 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
454 if ((NULL == id) || (NULL == system->hostkeys_data))
455 return GNUNET_SYSERR;
456 if (key_number >= system->total_hostkeys)
458 LOG (GNUNET_ERROR_TYPE_ERROR,
459 _("Key number %u does not exist\n"), key_number);
460 return GNUNET_SYSERR;
462 private_key = GNUNET_CRYPTO_rsa_decode_key (system->hostkeys_data +
463 (key_number * HOSTKEYFILESIZE),
465 if (NULL == private_key)
467 LOG (GNUNET_ERROR_TYPE_ERROR,
468 _("Error while decoding key %u\n"), key_number);
469 return GNUNET_SYSERR;
471 GNUNET_CRYPTO_rsa_key_get_public (private_key, &public_key);
472 GNUNET_CRYPTO_hash (&public_key,
473 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
475 GNUNET_CRYPTO_rsa_key_free (private_key);
481 * Structure for holding data to build new configurations from a configuration
487 * The system for which we are building configurations
489 struct GNUNET_TESTING_System *system;
492 * The configuration we are building
494 struct GNUNET_CONFIGURATION_Handle *cfg;
497 * The customized service home path for this peer
502 * build status - to signal error while building a configuration
509 * Function to iterate over options. Copies
510 * the options to the target configuration,
511 * updating PORT values as needed.
513 * @param cls the UpdateContext
514 * @param section name of the section
515 * @param option name of the option
516 * @param value value of the option
519 update_config (void *cls, const char *section, const char *option,
522 struct UpdateContext *uc = cls;
526 char *single_variable;
527 char *per_host_variable;
528 unsigned long long num_per_host;
531 if (GNUNET_OK != uc->status)
533 if (! ((0 == strcmp (option, "PORT"))
534 || (0 == strcmp (option, "UNIXPATH"))
535 || (0 == strcmp (option, "HOSTNAME"))))
537 GNUNET_asprintf (&single_variable, "single_%s_per_host", section);
538 GNUNET_asprintf (&per_host_variable, "num_%s_per_host", section);
539 if ((0 == strcmp (option, "PORT")) && (1 == SSCANF (value, "%u", &ival)))
543 GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
546 /* FIXME: What about UDP? */
547 new_port = GNUNET_TESTING_reserve_port (uc->system, GNUNET_YES);
550 uc->status = GNUNET_SYSERR;
553 GNUNET_snprintf (cval, sizeof (cval), "%u", new_port);
556 else if ((ival != 0) &&
558 GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
560 GNUNET_CONFIGURATION_get_value_number (uc->cfg, "testing",
564 /* GNUNET_snprintf (cval, sizeof (cval), "%u", */
565 /* ival + ctx->fdnum % num_per_host); */
567 GNUNET_break (0); /* FIXME */
570 if (0 == strcmp (option, "UNIXPATH"))
573 GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
576 GNUNET_snprintf (uval, sizeof (uval), "%s/%s.sock",
577 uc->service_home, section);
580 else if ((GNUNET_YES ==
581 GNUNET_CONFIGURATION_get_value_number (uc->cfg, "testing",
586 GNUNET_break(0); /* FIXME */
589 if ((0 == strcmp (option, "HOSTNAME")) && (NULL != uc->system->controller))
591 value = uc->system->controller;
593 GNUNET_free (single_variable);
594 GNUNET_free (per_host_variable);
595 GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, option, value);
600 * Section iterator to set ACCEPT_FROM in all sections
602 * @param cls the UpdateContext
603 * @param section name of the section
606 update_config_sections (void *cls,
609 struct UpdateContext *uc = cls;
610 char *orig_allowed_hosts;
614 GNUNET_CONFIGURATION_get_value_string (uc->cfg, section, "ACCEPT_FROM",
615 &orig_allowed_hosts))
617 orig_allowed_hosts = GNUNET_strdup ("127.0.0.1;");
619 if (NULL == uc->system->controller)
620 allowed_hosts = GNUNET_strdup (orig_allowed_hosts);
622 GNUNET_asprintf (&allowed_hosts, "%s%s;", orig_allowed_hosts,
623 uc->system->controller);
624 GNUNET_free (orig_allowed_hosts);
625 GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, "ACCEPT_FROM",
627 GNUNET_free (allowed_hosts);
632 * Create a new configuration using the given configuration
633 * as a template; ports and paths will be modified to select
634 * available ports on the local system. If we run
635 * out of "*port" numbers, return SYSERR.
637 * This is primarily a helper function used internally
638 * by 'GNUNET_TESTING_peer_configure'.
640 * @param system system to use to coordinate resource usage
641 * @param cfg template configuration to update
642 * @return GNUNET_OK on success, GNUNET_SYSERR on error - the configuration will
643 * be incomplete and should not be used there upon
646 GNUNET_TESTING_configuration_create (struct GNUNET_TESTING_System *system,
647 struct GNUNET_CONFIGURATION_Handle *cfg)
649 struct UpdateContext uc;
650 char *default_config;
654 uc.status = GNUNET_OK;
655 GNUNET_asprintf (&uc.service_home, "%s/%u", system->tmppath,
656 system->path_counter++);
657 GNUNET_asprintf (&default_config, "%s/config", uc.service_home);
658 GNUNET_CONFIGURATION_set_value_string (cfg, "PATHS", "DEFAULTCONFIG",
660 GNUNET_free (default_config);
661 GNUNET_CONFIGURATION_set_value_string (cfg, "PATHS", "SERVICEHOME",
663 /* make PORTs and UNIXPATHs unique */
664 GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
665 /* allow connections to services from system controller host */
666 GNUNET_CONFIGURATION_iterate_sections (cfg, &update_config_sections, &uc);
667 /* enable loopback-based connections between peers */
668 GNUNET_CONFIGURATION_set_value_string (cfg,
670 "USE_LOCALADDR", "YES");
671 GNUNET_free (uc.service_home);
677 * Configure a GNUnet peer. GNUnet must be installed on the local
678 * system and available in the PATH.
680 * @param system system to use to coordinate resource usage
681 * @param cfg configuration to use; will be UPDATED (to reflect needed
682 * changes in port numbers and paths)
683 * @param key_number number of the hostkey to use for the peer
684 * @param id identifier for the daemon, will be set, can be NULL
685 * @param emsg set to error message (set to NULL on success), can be NULL
686 * @return handle to the peer, NULL on error
688 struct GNUNET_TESTING_Peer *
689 GNUNET_TESTING_peer_configure (struct GNUNET_TESTING_System *system,
690 struct GNUNET_CONFIGURATION_Handle *cfg,
692 struct GNUNET_PeerIdentity *id,
695 struct GNUNET_TESTING_Peer *peer;
696 struct GNUNET_DISK_FileHandle *fd;
698 char hostkey_filename[128];
699 char *config_filename;
704 if (GNUNET_OK != GNUNET_TESTING_configuration_create (system, cfg))
706 GNUNET_asprintf (&emsg_,
707 _("Failed to create configuration for peer (not enough free ports?)\n"));
708 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_);
715 if (key_number >= system->total_hostkeys)
717 GNUNET_asprintf (&emsg_,
718 _("You attempted to create a testbed with more than %u hosts. Please precompute more hostkeys first.\n"),
719 (unsigned int) system->total_hostkeys);
720 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", emsg_);
728 (GNUNET_SYSERR == GNUNET_TESTING_hostkey_get (system, key_number, id)))
730 GNUNET_asprintf (&emsg_,
731 _("Failed to initialize hostkey for peer %u\n"),
732 (unsigned int) key_number);
733 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_);
740 GNUNET_assert (GNUNET_OK ==
741 GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS",
744 GNUNET_snprintf (hostkey_filename, sizeof (hostkey_filename), "%s/.hostkey",
746 GNUNET_free (service_home);
747 fd = GNUNET_DISK_file_open (hostkey_filename,
748 GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_WRITE,
749 GNUNET_DISK_PERM_USER_READ
750 | GNUNET_DISK_PERM_USER_WRITE);
756 if (HOSTKEYFILESIZE !=
757 GNUNET_DISK_file_write (fd, system->hostkeys_data
758 + (key_number * HOSTKEYFILESIZE),
761 GNUNET_asprintf (&emsg_,
762 _("Failed to write hostkey file for peer %u: %s\n"),
763 (unsigned int) key_number,
765 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_);
770 GNUNET_DISK_file_close (fd);
773 GNUNET_DISK_file_close (fd);
774 GNUNET_assert (GNUNET_OK ==
775 GNUNET_CONFIGURATION_get_value_string
776 (cfg, "PATHS", "DEFAULTCONFIG", &config_filename));
777 if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, config_filename))
779 GNUNET_asprintf (&emsg_,
780 _("Failed to write configuration file `%s' for peer %u: %s\n"),
782 (unsigned int) key_number,
784 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_);
789 GNUNET_free (config_filename);
792 peer = GNUNET_malloc (sizeof (struct GNUNET_TESTING_Peer));
793 peer->cfgfile = config_filename; /* Free in peer_destroy */
794 peer->main_binary = GNUNET_strdup ("gnunet-service-arm");
802 * @param peer peer to start
803 * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. peer already running)
806 GNUNET_TESTING_peer_start (struct GNUNET_TESTING_Peer *peer)
808 if (NULL != peer->main_process)
811 return GNUNET_SYSERR;
813 GNUNET_assert (NULL != peer->cfgfile);
814 peer->main_process = GNUNET_OS_start_process (GNUNET_YES, NULL, NULL,
820 if (NULL == peer->main_process)
822 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
823 _("Failed to start `%s': %s\n"),
826 return GNUNET_SYSERR;
835 * @param peer peer to stop
836 * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. peer not running)
839 GNUNET_TESTING_peer_stop (struct GNUNET_TESTING_Peer *peer)
841 if (NULL == peer->main_process)
844 return GNUNET_SYSERR;
846 (void) GNUNET_OS_process_kill (peer->main_process, SIGTERM);
847 GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (peer->main_process));
848 GNUNET_OS_process_destroy (peer->main_process);
849 peer->main_process = NULL;
855 * Destroy the peer. Releases resources locked during peer configuration.
856 * If the peer is still running, it will be stopped AND a warning will be
857 * printed (users of the API should stop the peer explicitly first).
859 * @param peer peer to destroy
862 GNUNET_TESTING_peer_destroy (struct GNUNET_TESTING_Peer *peer)
864 if (NULL != peer->main_process)
867 GNUNET_TESTING_peer_stop (peer);
869 GNUNET_free (peer->cfgfile);
870 GNUNET_free (peer->main_binary);
876 * Start a single peer and run a test using the testing library.
877 * Starts a peer using the given configuration and then invokes the
878 * given callback. This function ALSO initializes the scheduler loop
879 * and should thus be called directly from "main". The testcase
880 * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
882 * @param testdir only the directory name without any path. This is used for
883 * all service homes; the directory will be created in a temporary
884 * location depending on the underlying OS
885 * @param cfgfilename name of the configuration file to use;
886 * use NULL to only run with defaults
887 * @param tm main function of the testcase
888 * @param tm_cls closure for 'tm'
889 * @return 0 on success, 1 on error
892 GNUNET_TESTING_peer_run (const char *testdir,
893 const char *cfgfilename,
894 GNUNET_TESTING_TestMain tm,
897 return GNUNET_TESTING_service_run (testdir, "arm",
898 cfgfilename, tm, tm_cls);
903 * Structure for holding service data
905 struct ServiceContext
908 * The configuration of the peer in which the service is run
910 const struct GNUNET_CONFIGURATION_Handle *cfg;
913 * Callback to signal service startup
915 GNUNET_TESTING_TestMain tm;
918 * Closure for the above callback
925 * Structure for holding service data
927 struct RestartableServiceContext
930 * The configuration of the peer in which the service is run
932 const struct GNUNET_CONFIGURATION_Handle *cfg;
935 * Callback to signal service startup
937 GNUNET_TESTING_RestartableTestMain tm;
940 * The peer in which the service is run.
942 const struct GNUNET_TESTING_Peer *peer;
945 * Closure for the above callback
952 * Callback to be called when SCHEDULER has been started
954 * @param cls the ServiceContext
955 * @param tc the TaskContext
958 service_run_main (void *cls,
959 const struct GNUNET_SCHEDULER_TaskContext *tc)
961 struct ServiceContext *sc = cls;
963 sc->tm (sc->tm_cls, sc->cfg);
968 * Callback to be called when SCHEDULER has been started
970 * @param cls the ServiceContext
971 * @param tc the TaskContext
974 service_run_restartable_main (void *cls,
975 const struct GNUNET_SCHEDULER_TaskContext *tc)
977 struct RestartableServiceContext *sc = cls;
979 sc->tm (sc->tm_cls, sc->cfg, sc->peer);
984 * Start a single service (no ARM, except of course if the given
985 * service name is 'arm') and run a test using the testing library.
986 * Starts a service using the given configuration and then invokes the
987 * given callback. This function ALSO initializes the scheduler loop
988 * and should thus be called directly from "main". The testcase
989 * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
991 * This function is useful if the testcase is for a single service
992 * and if that service doesn't itself depend on other services.
994 * @param testdir only the directory name without any path. This is used for
995 * all service homes; the directory will be created in a temporary
996 * location depending on the underlying OS
997 * @param service_name name of the service to run
998 * @param cfgfilename name of the configuration file to use;
999 * use NULL to only run with defaults
1000 * @param tm main function of the testcase
1001 * @param tm_cls closure for 'tm'
1002 * @return 0 on success, 1 on error
1005 GNUNET_TESTING_service_run (const char *testdir,
1006 const char *service_name,
1007 const char *cfgfilename,
1008 GNUNET_TESTING_TestMain tm,
1011 struct ServiceContext sc;
1012 struct GNUNET_TESTING_System *system;
1013 struct GNUNET_TESTING_Peer *peer;
1014 struct GNUNET_CONFIGURATION_Handle *cfg;
1016 GNUNET_log_setup (testdir,
1019 system = GNUNET_TESTING_system_create (testdir, "127.0.0.1");
1022 cfg = GNUNET_CONFIGURATION_create ();
1023 if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, cfgfilename))
1025 LOG (GNUNET_ERROR_TYPE_ERROR,
1026 _("Failed to load configuration from %s\n"), cfgfilename);
1027 GNUNET_CONFIGURATION_destroy (cfg);
1028 GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1031 peer = GNUNET_TESTING_peer_configure (system, cfg, 0, NULL, NULL);
1034 GNUNET_CONFIGURATION_destroy (cfg);
1035 hostkeys_unload (system);
1036 GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1039 GNUNET_free (peer->main_binary);
1040 GNUNET_asprintf (&peer->main_binary, "gnunet-service-%s", service_name);
1041 if (GNUNET_OK != GNUNET_TESTING_peer_start (peer))
1043 GNUNET_TESTING_peer_destroy (peer);
1044 GNUNET_CONFIGURATION_destroy (cfg);
1045 GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1051 GNUNET_SCHEDULER_run (&service_run_main, &sc); /* Scheduler loop */
1052 if (GNUNET_OK != GNUNET_TESTING_peer_stop (peer))
1054 GNUNET_TESTING_peer_destroy (peer);
1055 GNUNET_CONFIGURATION_destroy (cfg);
1056 GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1059 GNUNET_TESTING_peer_destroy (peer);
1060 GNUNET_CONFIGURATION_destroy (cfg);
1061 GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1068 * See GNUNET_TESTING_service_run.
1069 * The only difference is that we handle the GNUNET_TESTING_Peer to
1070 * the RestartableTestMain, so that the peer can be destroyed and re-created
1071 * to simulate failure in tests.
1074 GNUNET_TESTING_service_run_restartable (const char *testdir,
1075 const char *service_name,
1076 const char *cfgfilename,
1077 GNUNET_TESTING_RestartableTestMain tm,
1080 struct RestartableServiceContext sc;
1081 struct GNUNET_TESTING_System *system;
1082 struct GNUNET_TESTING_Peer *peer;
1083 struct GNUNET_CONFIGURATION_Handle *cfg;
1085 GNUNET_log_setup (testdir,
1088 system = GNUNET_TESTING_system_create (testdir, "127.0.0.1");
1091 cfg = GNUNET_CONFIGURATION_create ();
1092 if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, cfgfilename))
1094 LOG (GNUNET_ERROR_TYPE_ERROR,
1095 _("Failed to load configuration from %s\n"), cfgfilename);
1096 GNUNET_CONFIGURATION_destroy (cfg);
1097 GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1100 peer = GNUNET_TESTING_peer_configure (system, cfg, 0, NULL, NULL);
1103 GNUNET_CONFIGURATION_destroy (cfg);
1104 hostkeys_unload (system);
1105 GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1108 GNUNET_free (peer->main_binary);
1109 GNUNET_asprintf (&peer->main_binary, "gnunet-service-%s", service_name);
1110 if (GNUNET_OK != GNUNET_TESTING_peer_start (peer))
1112 GNUNET_TESTING_peer_destroy (peer);
1113 GNUNET_CONFIGURATION_destroy (cfg);
1114 GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1121 GNUNET_SCHEDULER_run (&service_run_restartable_main, &sc); /* Scheduler loop */
1122 if (GNUNET_OK != GNUNET_TESTING_peer_stop (peer))
1124 GNUNET_TESTING_peer_destroy (peer);
1125 GNUNET_CONFIGURATION_destroy (cfg);
1126 GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1129 GNUNET_TESTING_peer_destroy (peer);
1130 GNUNET_CONFIGURATION_destroy (cfg);
1131 GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1137 * Sometimes we use the binary name to determine which specific
1138 * test to run. In those cases, the string after the last "_"
1139 * in 'argv[0]' specifies a string that determines the configuration
1140 * file or plugin to use.
1142 * This function returns the respective substring, taking care
1143 * of issues such as binaries ending in '.exe' on W32.
1145 * @param argv0 the name of the binary
1146 * @return string between the last '_' and the '.exe' (or the end of the string),
1147 * NULL if argv0 has no '_'
1150 GNUNET_TESTING_get_testname_from_underscore (const char *argv0)
1152 size_t slen = strlen (argv0) + 1;
1157 memcpy (sbuf, argv0, slen);
1158 ret = strrchr (sbuf, '_');
1161 ret++; /* skip underscore */
1162 dot = strchr (ret, '.');
1165 return GNUNET_strdup (ret);
1169 /* end of testing.c */