From b441997d46bfa7e722dc2cd939f79890ff6ca8fd Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Tue, 12 Jun 2012 22:26:07 +0000 Subject: added a command line tool for running and controlling services for testing --- src/testing/Makefile.am | 13 ++- src/testing/gnunet-testing-run-service.c | 187 +++++++++++++++++++++++++++++++ src/testing/testing.c | 113 +++++++++++++++++++ 3 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 src/testing/gnunet-testing-run-service.c (limited to 'src/testing') diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am index c8e477449..7ddc8e857 100644 --- a/src/testing/Makefile.am +++ b/src/testing/Makefile.am @@ -26,6 +26,17 @@ libgnunettesting_la_LDFLAGS = \ $(GN_LIB_LDFLAGS) \ -version-info 0:1:0 +bin_PROGRAMS = \ + gnunet-testing-run-service + + +gnunet_testing_run_service_SOURCES = \ + gnunet-testing-run-service.c + +gnunet_testing_run_service_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(GN_LIBINTL) check_PROGRAMS = \ test_testing_portreservation \ @@ -59,4 +70,4 @@ test_testing_servicestartup_LDADD = \ EXTRA_DIST = \ - test_testing_defaults.conf \ No newline at end of file + test_testing_defaults.conf diff --git a/src/testing/gnunet-testing-run-service.c b/src/testing/gnunet-testing-run-service.c new file mode 100644 index 000000000..514c0f420 --- /dev/null +++ b/src/testing/gnunet-testing-run-service.c @@ -0,0 +1,187 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + + +/** + * @file testing/gnunet-testing-run-service.c + * @brief tool to start a service for testing + * @author Florian Dold + * + * Start a peer with the service specified on the command line. + * Outputs the path to the temporary configuration file to stdout. + * + * The peer will run until this program is killed, + * or stdin is closed. + * + * This executable is intended to be used by gnunet-java, in order to reliably + * start and stop services for test cases. + */ + +#include "platform.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_program_lib.h" +#include "gnunet_util_lib.h" +#include "gnunet_signal_lib.h" +#include "gnunet_testing_lib-new.h" +#include "gnunet_os_lib.h" + + +static struct GNUNET_DISK_FileHandle fh; +static char *tmpfilename = NULL; +static GNUNET_SCHEDULER_TaskIdentifier tid = GNUNET_SCHEDULER_NO_TASK; +static struct GNUNET_TESTING_Peer *my_peer = NULL; + + +#define LOG(kind,...) \ + GNUNET_log_from (kind, "gnunettestingnew", __VA_ARGS__) + + +/** + * Cleanup called by signal handlers and when stdin is closed. + * Removes the temporary file with the configuration and shuts down the scheduler. + */ +void +cleanup (void) +{ + if (NULL != tmpfilename) + { + remove (tmpfilename); + } + GNUNET_SCHEDULER_shutdown (); +} + +/** + * Called whenever we can read stdin non-blocking + */ +void +stdin_cb (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + int c; + + if (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) + { + return; + } + if (GNUNET_SCHEDULER_REASON_READ_READY & tc->reason) + { + c = getchar (); + if (EOF == c) + { + tid = GNUNET_SCHEDULER_NO_TASK; + cleanup (); + } + else + { + if (c == 'r') + { + GNUNET_TESTING_peer_stop(my_peer); + GNUNET_TESTING_peer_start(my_peer); + printf("restarted\n"); + } + tid = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, &fh, &stdin_cb, NULL); + } + return; + } + GNUNET_break (0); +} + +/** + * Main function called by the testing library. + * Executed inside a running scheduler. + */ +void +testing_main (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg, + const struct GNUNET_TESTING_Peer *peer) +{ + my_peer = peer; + tmpfilename = tmpnam (NULL); + if (NULL == tmpfilename) + { + GNUNET_break (0); + cleanup (); + return; + } + + if (GNUNET_SYSERR == + GNUNET_CONFIGURATION_write((struct GNUNET_CONFIGURATION_Handle *) cfg, tmpfilename)) + { + GNUNET_break (0); + return; + } + + printf("%s\n", tmpfilename); + fflush(stdout); + + GNUNET_break(NULL != GNUNET_SIGNAL_handler_install(SIGTERM, &cleanup)); + GNUNET_break(NULL != GNUNET_SIGNAL_handler_install(SIGINT, &cleanup)); + + fh.fd = 0; /* 0=stdin */ + tid = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, &fh, &stdin_cb, NULL); +} + + + +/** + * The main function. + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, char *const *argv) +{ + static const struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_HELP("tool to start a service for testing"), + GNUNET_GETOPT_OPTION_END + }; + int arg_start; + int ret; + + struct GNUNET_OS_Process *p; + + p = GNUNET_OS_start_process(GNUNET_YES, NULL, NULL, "sgjldsjg"); + + if (p != NULL) { + printf("bogus!\n"); + } + + + return 0; + + arg_start = GNUNET_GETOPT_run("gnunet-testing-run-service", options, argc, argv); + + if (arg_start == GNUNET_SYSERR) { + return 1; + } + + if (arg_start != 1 || argc != 2) + { + fprintf (stderr, "Invalid number of arguments\n"); + return 1; + } + + ret = GNUNET_TESTING_service_run_restartable ("gnunet_service_test", argv[1], + NULL, &testing_main, NULL); + + printf ("bye\n"); + + return ret; +} diff --git a/src/testing/testing.c b/src/testing/testing.c index 9e18628c1..fca969b19 100644 --- a/src/testing/testing.c +++ b/src/testing/testing.c @@ -921,6 +921,33 @@ struct ServiceContext }; +/** + * Structure for holding service data + */ +struct RestartableServiceContext +{ + /** + * The configuration of the peer in which the service is run + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Callback to signal service startup + */ + GNUNET_TESTING_RestartableTestMain tm; + + /** + * The peer in which the service is run. + */ + const struct GNUNET_TESTING_Peer *peer; + + /** + * Closure for the above callback + */ + void *tm_cls; +}; + + /** * Callback to be called when SCHEDULER has been started * @@ -937,6 +964,22 @@ service_run_main (void *cls, } +/** + * Callback to be called when SCHEDULER has been started + * + * @param cls the ServiceContext + * @param tc the TaskContext + */ +static void +service_run_restartable_main (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct RestartableServiceContext *sc = cls; + + sc->tm (sc->tm_cls, sc->cfg, sc->peer); +} + + /** * Start a single service (no ARM, except of course if the given * service name is 'arm') and run a test using the testing library. @@ -1020,6 +1063,76 @@ GNUNET_TESTING_service_run (const char *testdir, } + +/** + * See GNUNET_TESTING_service_run. + * The only difference is that we handle the GNUNET_TESTING_Peer to + * the RestartableTestMain, so that the peer can be destroyed and re-created + * to simulate failure in tests. + */ +int +GNUNET_TESTING_service_run_restartable (const char *testdir, + const char *service_name, + const char *cfgfilename, + GNUNET_TESTING_RestartableTestMain tm, + void *tm_cls) +{ + struct RestartableServiceContext sc; + struct GNUNET_TESTING_System *system; + struct GNUNET_TESTING_Peer *peer; + struct GNUNET_CONFIGURATION_Handle *cfg; + + GNUNET_log_setup (testdir, + "WARNING", + NULL); + system = GNUNET_TESTING_system_create (testdir, "127.0.0.1"); + if (NULL == system) + return 1; + cfg = GNUNET_CONFIGURATION_create (); + if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, cfgfilename)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Failed to load configuration from %s\n"), cfgfilename); + GNUNET_CONFIGURATION_destroy (cfg); + GNUNET_TESTING_system_destroy (system, GNUNET_YES); + return 1; + } + peer = GNUNET_TESTING_peer_configure (system, cfg, 0, NULL, NULL); + if (NULL == peer) + { + GNUNET_CONFIGURATION_destroy (cfg); + hostkeys_unload (system); + GNUNET_TESTING_system_destroy (system, GNUNET_YES); + return 1; + } + GNUNET_free (peer->main_binary); + GNUNET_asprintf (&peer->main_binary, "gnunet-service-%s", service_name); + if (GNUNET_OK != GNUNET_TESTING_peer_start (peer)) + { + GNUNET_TESTING_peer_destroy (peer); + GNUNET_CONFIGURATION_destroy (cfg); + GNUNET_TESTING_system_destroy (system, GNUNET_YES); + return 1; + } + sc.cfg = cfg; + sc.tm = tm; + sc.tm_cls = tm_cls; + sc.peer = peer; + GNUNET_SCHEDULER_run (&service_run_restartable_main, &sc); /* Scheduler loop */ + if (GNUNET_OK != GNUNET_TESTING_peer_stop (peer)) + { + GNUNET_TESTING_peer_destroy (peer); + GNUNET_CONFIGURATION_destroy (cfg); + GNUNET_TESTING_system_destroy (system, GNUNET_YES); + return 1; + } + GNUNET_TESTING_peer_destroy (peer); + GNUNET_CONFIGURATION_destroy (cfg); + GNUNET_TESTING_system_destroy (system, GNUNET_YES); + return 0; +} + + /** * Sometimes we use the binary name to determine which specific * test to run. In those cases, the string after the last "_" -- cgit v1.2.3