From 45fb407b4934c3e97dec8b79de0cdd25be11261e Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 14 Jul 2024 16:32:55 +0200 Subject: [PATCH] first test against test framework --- src/include/microhttpd2.h | 18 +- src/tests/basic/Makefile.am | 18 + src/tests/basic/libtest.c | 583 ++++++++++++++++++++++++++ src/tests/basic/libtest.h | 223 ++++++++++ src/tests/basic/libtest_convenience.c | 124 ++++++ src/tests/basic/test_basic_checks.c | 2 +- src/tests/basic/test_client_server.c | 55 +++ 7 files changed, 1021 insertions(+), 2 deletions(-) create mode 100644 src/tests/basic/libtest.c create mode 100644 src/tests/basic/libtest.h create mode 100644 src/tests/basic/libtest_convenience.c create mode 100644 src/tests/basic/test_client_server.c diff --git a/src/include/microhttpd2.h b/src/include/microhttpd2.h index 3a1776be..343ec46c 100644 --- a/src/include/microhttpd2.h +++ b/src/include/microhttpd2.h @@ -5515,6 +5515,22 @@ MHD_response_from_buffer ( MHD_FN_PAR_IN_SIZE_ (3,2); +/** + * Create a response object with body that is a + * statically allocated buffer that never needs to + * be freed as its lifetime exceeds that of the + * daemon. + * + * The response object can be extended with header information and then be used + * any number of times. + * @param sc status code to use for the response + * @param len number of bytes in @a buf + * @param buf buffer with response payload + */ +#define MHD_response_from_buffer_static(sc, len, buf) \ + MHD_response_from_buffer (sc, len, buf, NULL, NULL) + + /** * Create a response object with empty (zero size) body. * @@ -5523,7 +5539,7 @@ MHD_FN_PAR_IN_SIZE_ (3,2); * @param sc status code to use for the response */ #define MHD_response_from_empty(sc) \ - MHD_response_from_buffer (sc, 0, "", NULL, NULL) + MHD_response_from_buffer_static (sc, 0, "") /** diff --git a/src/tests/basic/Makefile.am b/src/tests/basic/Makefile.am index eee19e17..6b2d0e4d 100644 --- a/src/tests/basic/Makefile.am +++ b/src/tests/basic/Makefile.am @@ -3,6 +3,7 @@ EMPTY_ITEM = AM_CPPFLAGS = \ -I$(top_srcdir)/src/include \ + -I$(top_srcdir)/src/mhd2 \ -DMHD_CPU_COUNT=$(CPU_COUNT) \ $(CPPFLAGS_ac) @@ -16,6 +17,16 @@ if USE_COVERAGE AM_CFLAGS += -fprofile-arcs -ftest-coverage endif +noinst_LTLIBRARIES = \ + libmhdt.la + +libmhdt_la_SOURCES = \ + libtest.c libtest.h \ + libtest_convenience.c +libmhdt_la_LIBADD = \ + -lpthread \ + -lcurl + LDADD = $(top_builddir)/src/mhd2/libmicrohttpd2.la $(top_builddir)/src/mhd2/libmicrohttpd2.la: $(top_builddir)/src/mhd2/Makefile @@ -23,6 +34,7 @@ $(top_builddir)/src/mhd2/libmicrohttpd2.la: $(top_builddir)/src/mhd2/Makefile $(am__cd) $(top_builddir)/src/mhd2 && $(MAKE) $(AM_MAKEFLAGS) libmicrohttpd2.la check_PROGRAMS = \ + test_client_server \ test_create_destroy \ test_create_start_destroy \ test_create_destroy_ipv4 \ @@ -103,6 +115,12 @@ endif TESTS = $(check_PROGRAMS) +test_client_server_SOURCES = \ + test_client_server.c +test_client_server_LDADD = \ + libmhdt.la \ + $(top_builddir)/src/mhd2/libmicrohttpd2.la + # The universal sources used in all tests basic_test_sources = test_basic_checks.c diff --git a/src/tests/basic/libtest.c b/src/tests/basic/libtest.c new file mode 100644 index 00000000..f009a25e --- /dev/null +++ b/src/tests/basic/libtest.c @@ -0,0 +1,583 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Christian Grothoff + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file libtest.c + * @brief testing harness with clients against server + * @author Christian Grothoff + */ +#include +#include +#include +#include +#include +#include "microhttpd2.h" +#include "libtest.h" + +/** + * A semaphore. + */ +struct Semaphore +{ + /** + * Mutex for the semaphore. + */ + pthread_mutex_t mutex; + + /** + * Condition variable for the semaphore. + */ + pthread_cond_t cv; + + /** + * Counter of the semaphore. + */ + unsigned int ctr; +}; + + +/** + * Check that @a cond is true, otherwise abort(). + * + * @param cond condition to check + * @param filename filename to log + * @param line line number to log + */ +static void +test_check_ (bool cond, + const char *filename, + unsigned int line) +{ + if (! cond) + { + fprintf (stderr, + "Assertion failed at %s:%u\n", + filename, + line); + abort (); + } +} + + +/** + * Checks that @a cond is true and otherwise aborts. + * + * @param cond condition to check + */ +#define test_check(cond) \ + test_check_ (cond, __FILE__, __LINE__) + + +/** + * Initialize a semaphore @a sem with a value of @a val. + * + * @param[out] sem semaphore to initialize + * @param val initial value of the semaphore + */ +static void +semaphore_create (struct Semaphore *sem, + unsigned int val) +{ + test_check (0 == + pthread_mutex_init (&sem->mutex, + NULL)); + test_check (0 == + pthread_cond_init (&sem->cv, + NULL)); + sem->ctr = val; +} + + +/** + * Decrement semaphore, blocks until this is possible. + * + * @param[in,out] sem semaphore to decrement + */ +static void +semaphore_down (struct Semaphore *sem) +{ + test_check (0 == pthread_mutex_lock (&sem->mutex)); + while (0 == sem->ctr) + { + pthread_cond_wait (&sem->cv, + &sem->mutex); + } + sem->ctr--; + test_check (0 == pthread_mutex_unlock (&sem->mutex)); +} + + +/** + * Increment semaphore, blocks until this is possible. + * + * @param[in,out] sem semaphore to decrement + */ +static void +semaphore_up (struct Semaphore *sem) +{ + test_check (0 == pthread_mutex_lock (&sem->mutex)); + sem->ctr++; + test_check (0 == pthread_mutex_unlock (&sem->mutex)); + pthread_cond_signal (&sem->cv); +} + + +/** + * Release resources used by @a sem. + * + * @param[in] sem semaphore to release (except the memory itself) + */ +static void +semaphore_destroy (struct Semaphore *sem) +{ + test_check (0 == pthread_cond_destroy (&sem->cv)); + test_check (0 == pthread_mutex_destroy (&sem->mutex)); +} + + +/** + * Context for the implementation of the HTTP server. + */ +struct ServerContext +{ + /** + * Semaphore the client raises when it goes into the + * next phase. + */ + struct Semaphore client_sem; + + /** + * Semaphore the server raises when it goes into the + * next phase. + */ + struct Semaphore server_sem; + + /** + * Current phase of the server. + */ + const struct MHDT_Phase *phase; + + /** + * Main function to run the server. + */ + MHDT_ServerRunner run_cb; + + /** + * Closure for @e run_cb. + */ + void *run_cb_cls; + + /** + * The daemon we are running. + */ + struct MHD_Daemon *d; + + /** + * Signal for server termination. + */ + int finsig; +}; + + +/** + * A client has requested the given url using the given method + * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT, + * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). + * If @a upload_size is not zero and response action is provided by this + * callback, then upload will be discarded and the stream (the connection for + * HTTP/1.1) will be closed after sending the response. + * + * @param cls argument given together with the function + * pointer when the handler was registered with MHD + * @param request the request object + * @param path the requested uri (without arguments after "?") + * @param method the HTTP method used (#MHD_HTTP_METHOD_GET, + * #MHD_HTTP_METHOD_PUT, etc.) + * @param upload_size the size of the message upload content payload, + * #MHD_SIZE_UNKNOWN for chunked uploads (if the + * final chunk has not been processed yet) + * @return action how to proceed, NULL + * if the request must be aborted due to a serious + * error while handling the request (implies closure + * of underling data stream, for HTTP/1.1 it means + * socket closure). + */ +static const struct MHD_Action * +server_req_cb (void *cls, + struct MHD_Request *MHD_RESTRICT request, + const struct MHD_String *MHD_RESTRICT path, + enum MHD_HTTP_Method method, + uint_fast64_t upload_size) +{ + struct ServerContext *sc = cls; + + if (NULL == sc->phase->label) + return NULL; + return sc->phase->server_cb (sc->phase->server_cb_cls, + request, + path, + method, + upload_size); +} + + +/** + * Closure for run_single_client() + */ +struct ClientContext +{ + /** + * Test phase to run. + */ + const struct MHDT_Phase *phase; + + /** + * Phase and client specific context. + */ + struct MHDT_PhaseContext pc; + + /** + * Pipe to use to signal that the thread has + * finished. + */ + int p2; + + /** + * Set to true on success. + */ + bool status; +}; + + +/** + * Runs the logic for a single client in a thread. + * + * @param cls a `struct ClientContext` + * @return NULL + */ +static void * +run_single_client (void *cls) +{ + struct ClientContext *cc = cls; + const char *err; + + err = cc->phase->client_cb (cc->phase->client_cb_cls, + &cc->pc); + if (NULL != err) + { + fprintf (stderr, + "Client %u failed in phase %s: %s\n", + cc->pc.client_id, + cc->phase->label, + err); + /* This is a blocking write, thus must succeed */ + test_check (1 == + write (cc->p2, + "e", + 1)); + return NULL; + } + cc->status = true; + /* This is a blocking write, thus must succeed */ + test_check (1 == + write (cc->p2, + "s", + 1)); + return NULL; +} + + +/** + * Creates a pipe with a non-blocking read end. + * + * @param p pipe to initialize + */ +static void +make_pipe (int p[2]) +{ + int flags; + + test_check (0 == + pipe (p)); + flags = fcntl (p[0], + F_GETFL); + flags |= O_NONBLOCK; + test_check (0 == + fcntl (p[0], + F_SETFL, + flags)); +} + + +/** + * Run client processes for the given test @a phase + * + * @param phase test phase to run + * @param pc context to give to clients + */ +static bool +run_client_phase (const struct MHDT_Phase *phase, + const struct MHDT_PhaseContext *pc) +{ + unsigned int num_clients + = (0 == phase->num_clients) + ? 1 + : phase->num_clients; + unsigned int clients_left = 0; + struct ClientContext cctxs[num_clients]; + pthread_t clients[num_clients]; + int p[2]; + unsigned int i; + bool ret = true; + + make_pipe (p); + for (i = 0; itimeout_ms - 1; i>0; i--) + { + struct timespec ms = { + .tv_nsec = 1000 * 1000 + }; + char c; + + nanosleep (&ms, + NULL); + /* This is a non-blocking read */ + while (1 == read (p[0], + &c, + 1)) + clients_left--; + if (0 == clients_left) + break; + } + if (0 != clients_left) + { + fprintf (stderr, + "Timeout (%u ms) in phase %s: %u clients still running\n", + phase->timeout_ms, + phase->label, + clients_left); + exit (1); + } +cleanup: + for (i = 0; iphase->label; i++) + { + semaphore_down (&ctx->client_sem); + ctx->phase++; + semaphore_up (&ctx->server_sem); + } + return NULL; +} + + +/** + * Thread that runs the MHD daemon. + * + * @param cls a `struct ServerContext` + * @return NULL + */ +static void * +server_run_logic (void *cls) +{ + struct ServerContext *ctx = cls; + + ctx->run_cb (ctx->run_cb_cls, + ctx->finsig, + ctx->d); + return NULL; +} + + +int +MHDT_test (MHDT_ServerSetup ss_cb, + void *ss_cb_cls, + MHDT_ServerRunner run_cb, + void *run_cb_cls, + const struct MHDT_Phase *phases) +{ + struct ServerContext ctx = { + .run_cb = run_cb, + .run_cb_cls = run_cb_cls, + .phase = &phases[0] + }; + struct MHD_Daemon *d; + int res; + const char *err; + enum MHD_StatusCode sc; + pthread_t server_phase_thr; + pthread_t server_run_thr; + struct MHDT_PhaseContext pc; + char base_url[128]; + unsigned int i; + int p[2]; + + make_pipe (p); + semaphore_create (&ctx.server_sem, + 0); + semaphore_create (&ctx.client_sem, + 0); + d = MHD_daemon_create (&server_req_cb, + &ctx); + if (NULL == d) + exit (77); + err = ss_cb (ss_cb_cls, + d); + if (NULL != err) + { + fprintf (stderr, + "Failed to setup server: %s\n", + err); + return 1; + } + sc = MHD_daemon_start (d); + if (MHD_SC_OK != sc) + { + fprintf (stderr, + "Failed to start server: %s\n", + err); + return 1; + } + { + union MHD_DaemonInfoFixedData info; + enum MHD_StatusCode sc; + + sc = MHD_daemon_get_info_fixed ( + d, + MHD_DAEMON_INFO_FIXED_BIND_PORT, + &info); + test_check (MHD_SC_OK == sc); + snprintf (base_url, + sizeof (base_url), + "http://localhost:%u/", + (unsigned int) info.v_port); + pc.base_url = base_url; + } + if (0 != pthread_create (&server_phase_thr, + NULL, + &server_phase_logic, + &ctx)) + { + fprintf (stderr, + "Failed to start server phase thread: %s\n", + strerror (errno)); + return 77; + } + ctx.finsig = p[0]; + ctx.d = d; + if (0 != pthread_create (&server_run_thr, + NULL, + &server_run_logic, + &ctx)) + { + fprintf (stderr, + "Failed to start server run thread: %s\n", + strerror (errno)); + return 77; + } + // FIXME: start some thread to run the actual server! + + for (i = 0; NULL == phases[i].label; i++) + { + if (! run_client_phase (&phases[i], + &pc)) + { + res = 1; + goto cleanup; + } + /* client is done with phase */ + semaphore_up (&ctx.client_sem); + /* wait for server to have moved to new phase */ + semaphore_down (&ctx.server_sem); + } + res = 0; +cleanup: + /* stop thread that runs the actual server */ + { + void *res; + + test_check (1 == + write (p[1], + "e", + 1)); + test_check (0 == + pthread_join (server_run_thr, + &res)); + } + { + void *res; + + /* Unblock the #server_phase_logic() even if we had + an error */ + for (i = 0; NULL == phases[i].label; i++) + semaphore_up (&ctx.client_sem); + test_check (0 == + pthread_join (server_phase_thr, + &res)); + } + MHD_daemon_destroy (d); + semaphore_destroy (&ctx.client_sem); + semaphore_destroy (&ctx.server_sem); + test_check (0 == close (p[0])); + test_check (0 == close (p[1])); + return res; +} diff --git a/src/tests/basic/libtest.h b/src/tests/basic/libtest.h new file mode 100644 index 00000000..4f0187a1 --- /dev/null +++ b/src/tests/basic/libtest.h @@ -0,0 +1,223 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Christian Grothoff + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file libtest.h + * @brief testing harness with clients against server + * @author Christian Grothoff + */ +#ifndef LIBTEST_H +#define LIBTEST_H + +#include +#include +#include +#include + +/** + * Information about the current phase. + */ +struct MHDT_PhaseContext +{ + /** + * Base URL of the server + */ + const char *base_url; + + /** + * Specific client we are running. + */ + unsigned int client_id; +}; + + +/** + * Function called to run some client logic against + * the server. + * + * @param cls closure + * @param pc context for the client + * @return error message, NULL on success + */ +typedef const char * +(*MHDT_ClientLogic)(void *cls, + const struct MHDT_PhaseContext *pc); + + +/** + * Run request against the base URL and expect the + * string in @a cls to be returned + * + * @param cls closure with text string to be returned + * @param pc context for the client + * @return error message, NULL on success + */ +const char * +MHDT_client_get_root (void *cls, + const struct MHDT_PhaseContext *pc); + + +/** + * A phase defines some server and client-side + * behaviors to execute. + */ +struct MHDT_Phase +{ + + /** + * Name of the phase, for debugging/logging. + */ + const char *label; + + /** + * Logic for the MHD server for this phase. + */ + MHD_RequestCallback server_cb; + + /** + * Closure for @e server_cb. + */ + void *server_cb_cls; + + /** + * Logic for the CURL client for this phase. + */ + MHDT_ClientLogic client_cb; + + /** + * Closure for @e client_cb. + */ + void *client_cb_cls; + + /** + * How long is the phase allowed to run at most before + * timing out. 0 for no timeout. + */ + unsigned int timeout_ms; + + /** + * How many clients should be run in parallel. + * 0 to run just one client. + */ + unsigned int num_clients; +}; + + +/** + * Returns the text from @a cls as the response to any + * request. + * + * @param cls argument given together with the function + * pointer when the handler was registered with MHD + * @param request the request object + * @param path the requested uri (without arguments after "?") + * @param method the HTTP method used (#MHD_HTTP_METHOD_GET, + * #MHD_HTTP_METHOD_PUT, etc.) + * @param upload_size the size of the message upload content payload, + * #MHD_SIZE_UNKNOWN for chunked uploads (if the + * final chunk has not been processed yet) + * @return action how to proceed, NULL + * if the request must be aborted due to a serious + * error while handling the request (implies closure + * of underling data stream, for HTTP/1.1 it means + * socket closure). + */ +const struct MHD_Action * +MHDT_server_reply_text ( + void *cls, + struct MHD_Request *MHD_RESTRICT request, + const struct MHD_String *MHD_RESTRICT path, + enum MHD_HTTP_Method method, + uint_fast64_t upload_size); + + +/** + * Initialize options for an MHD daemon for a test. + * + * @param cls closure + * @param[in,out] d daemon to initialize + * @return error message, NULL on success + */ +typedef const char * +(*MHDT_ServerSetup)(void *cls, + struct MHD_Daemon *d); + + +/** + * Initialize MHD daemon without any special + * options, binding to any free port. + * + * @param cls closure + * @param[in,out] d daemon to initialize + * @return error message, NULL on success + */ +const char * +MHDT_server_setup_minimal (void *cls, + struct MHD_Daemon *d); + + +/** + * Function that runs an MHD daemon until + * a read() against @a finsig succeeds. + * + * @param cls closure + * @param finsig fd to read from to detect termination request + * @param[in,out] d daemon to run + */ +typedef void +(*MHDT_ServerRunner)(void *cls, + int finsig, + struct MHD_Daemon *d); + + +/** + * Function that runs an MHD daemon in blocking mode until + * a read() against @a finsig succeeds. + * + * @param cls closure + * @param finsig fd to read from to detect termination request + * @param[in,out] d daemon to run + */ +void +MHDT_server_run_blocking (void *cls, + int finsig, + struct MHD_Daemon *d); + + +/** + * Run test suite with @a phases for a daemon initialized + * using @a ss_cb on the local machine. + * + * @param ss_cb setup logic for the daemon + * @param ss_cb_cls closure for @a ss_cb + * @param run_cb runs the daemon + * @param run_cb_cls closure for @a run_cb + * @param phases test phases to run in child processes + * @return 0 on success, 77 if test was skipped, + * error code otherwise + */ +int +MHDT_test (MHDT_ServerSetup ss_cb, + void *ss_cb_cls, + MHDT_ServerRunner run_cb, + void *run_cb_cls, + const struct MHDT_Phase *phases); + +#endif diff --git a/src/tests/basic/libtest_convenience.c b/src/tests/basic/libtest_convenience.c new file mode 100644 index 00000000..a0181901 --- /dev/null +++ b/src/tests/basic/libtest_convenience.c @@ -0,0 +1,124 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Christian Grothoff + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file libtest_convenience.c + * @brief convenience functions for libtest users + * @author Christian Grothoff + */ +#include +#include +#include +#include +#include +#include "microhttpd2.h" +#include "libtest.h" +#include + + +const char * +MHDT_server_setup_minimal (void *cls, + struct MHD_Daemon *d) +{ + if (MHD_SC_OK != + MHD_DAEMON_SET_OPTIONS (d, + MHD_D_OPTION_BIND_PORT (MHD_AF_DUAL, + 0))) + return "Failed to bind to port 0!"; + return NULL; +} + + +void +MHDT_server_run_blocking (void *cls, + int finsig, + struct MHD_Daemon *d) +{ + char e; + + while (-1 == + read (finsig, + &e, + 1)) + { + if (EAGAIN != errno) + { + fprintf (stderr, + "Failure reading termination signal: %s\n", + strerror (errno)); + break; + } + if (MHD_SC_OK != + MHD_daemon_process_blocking (d, + 1000)) + { + fprintf (stderr, + "Failure running MHD_daemon_process_blocking()\n"); + break; + } + } +} + + +const struct MHD_Action * +MHDT_server_reply_text ( + void *cls, + struct MHD_Request *MHD_RESTRICT request, + const struct MHD_String *MHD_RESTRICT path, + enum MHD_HTTP_Method method, + uint_fast64_t upload_size) +{ + const char *text = cls; + + return MHD_action_from_response ( + request, + MHD_response_from_buffer_static (MHD_HTTP_STATUS_OK, + strlen (text), + text)); +} + + +const char * +MHDT_client_get_root ( + void *cls, + const struct MHDT_PhaseContext *pc) +{ + const char *text = cls; + CURL *c; + CURLcode res; + + c = curl_easy_init (); + if (NULL == c) + return "Failed to initialize Curl handle"; + if (CURLE_OK != + curl_easy_setopt (c, + CURLOPT_URL, + pc->base_url)) + { + curl_easy_cleanup (c); + return "Failed to set URL for curl request"; + } + res = curl_easy_perform (c); + curl_easy_cleanup (c); + if (CURLE_OK != res) + return "Failed to fetch URL"; + (void) text; // FIXME: check data returned was 'text' + return NULL; +} diff --git a/src/tests/basic/test_basic_checks.c b/src/tests/basic/test_basic_checks.c index 9b5549c8..9f498a50 100644 --- a/src/tests/basic/test_basic_checks.c +++ b/src/tests/basic/test_basic_checks.c @@ -19,7 +19,7 @@ */ /** - * @file test_start_destroy.c + * @file test_basic_checks.c * @brief test for create, start and destroy * @author Karlson2k (Evgeny Grin) */ diff --git a/src/tests/basic/test_client_server.c b/src/tests/basic/test_client_server.c new file mode 100644 index 00000000..425cff6c --- /dev/null +++ b/src/tests/basic/test_client_server.c @@ -0,0 +1,55 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2016, 2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file test_client_server.c + * @brief test with client against server + * @author Christian Grothoff + */ +#include +#include "libtest.h" + + +int +main (int argc, char *argv[]) +{ + struct MHDT_Phase phases[] = { + { + .label = "simple get", + .server_cb = &MHDT_server_reply_text, + .server_cb_cls = "Hello world", + .client_cb = &MHDT_client_get_root, + .client_cb_cls = "Hello world", + .timeout_ms = 5, + .num_clients = 10 + }, + { + .label = NULL, + }, + }; + (void) argc; /* Unused. Silence compiler warning. */ + (void) argv; /* Unused. Silence compiler warning. */ + + return MHDT_test (&MHDT_server_setup_minimal, + NULL, + &MHDT_server_run_blocking, + NULL, + phases); +}