diff options
author | Cy Schubert <cy@FreeBSD.org> | 2023-11-14 23:02:42 +0000 |
---|---|---|
committer | Cy Schubert <cy@FreeBSD.org> | 2023-11-14 23:02:42 +0000 |
commit | 5223d1d95fddcef6f9a36e264a5800bd907ade8b (patch) | |
tree | 818b1eba912c588e39058586485699385c3179fe /test | |
parent | cbc620a473ce23d882ba3e9f91ec0c6c12dcd239 (diff) |
Diffstat (limited to 'test')
38 files changed, 2838 insertions, 755 deletions
diff --git a/test/bench.c b/test/bench.c index 214479c1ff31..f2af4d3f30d1 100644 --- a/test/bench.c +++ b/test/bench.c @@ -34,6 +34,7 @@ */ #include "event2/event-config.h" +#include "../util-internal.h" #include <sys/types.h> #include <sys/stat.h> @@ -64,10 +65,12 @@ #include <event.h> #include <evutil.h> -static int count, writes, fired, failures; +static ev_ssize_t count, fired; +static int writes, failures; static evutil_socket_t *pipes; static int num_pipes, num_active, num_writes; static struct event *events; +static struct event_base *base; static void @@ -103,11 +106,11 @@ run_once(void) for (cp = pipes, i = 0; i < num_pipes; i++, cp += 2) { if (event_initialized(&events[i])) event_del(&events[i]); - event_set(&events[i], cp[0], EV_READ | EV_PERSIST, read_cb, (void *)(ev_intptr_t) i); + event_assign(&events[i], base, cp[0], EV_READ | EV_PERSIST, read_cb, (void *)(ev_intptr_t) i); event_add(&events[i], NULL); } - event_loop(EVLOOP_ONCE | EVLOOP_NONBLOCK); + event_base_loop(base, EVLOOP_ONCE | EVLOOP_NONBLOCK); fired = 0; space = num_pipes / num_active; @@ -117,15 +120,18 @@ run_once(void) count = 0; writes = num_writes; - { int xcount = 0; - evutil_gettimeofday(&ts, NULL); - do { - event_loop(EVLOOP_ONCE | EVLOOP_NONBLOCK); - xcount++; - } while (count != fired); - evutil_gettimeofday(&te, NULL); - - if (xcount != count) fprintf(stderr, "Xcount: %d, Rcount: %d\n", xcount, count); + { + int xcount = 0; + evutil_gettimeofday(&ts, NULL); + do { + event_base_loop(base, EVLOOP_ONCE | EVLOOP_NONBLOCK); + xcount++; + } while (count != fired); + evutil_gettimeofday(&te, NULL); + + if (xcount != count) + fprintf(stderr, "Xcount: %d, Rcount: " EV_SSIZE_FMT "\n", + xcount, count); } evutil_timersub(&te, &ts, &te); @@ -136,12 +142,15 @@ run_once(void) int main(int argc, char **argv) { -#ifdef HAVE_SETRLIMIT +#ifdef EVENT__HAVE_SETRLIMIT struct rlimit rl; #endif int i, c; struct timeval *tv; evutil_socket_t *cp; + const char **methods; + const char *method = NULL; + struct event_config *cfg = NULL; #ifdef _WIN32 WSADATA WSAData; @@ -150,7 +159,7 @@ main(int argc, char **argv) num_pipes = 100; num_active = 1; num_writes = num_pipes; - while ((c = getopt(argc, argv, "n:a:w:")) != -1) { + while ((c = getopt(argc, argv, "n:a:w:m:l")) != -1) { switch (c) { case 'n': num_pipes = atoi(optarg); @@ -161,13 +170,23 @@ main(int argc, char **argv) case 'w': num_writes = atoi(optarg); break; + case 'm': + method = optarg; + break; + case 'l': + methods = event_get_supported_methods(); + fprintf(stdout, "Using Libevent %s. Available methods are:\n", + event_get_version()); + for (i = 0; methods[i] != NULL; ++i) + printf(" %s\n", methods[i]); + exit(0); default: fprintf(stderr, "Illegal argument \"%c\"\n", c); exit(1); } } -#ifdef HAVE_SETRLIMIT +#ifdef EVENT__HAVE_SETRLIMIT rl.rlim_cur = rl.rlim_max = num_pipes * 2 + 50; if (setrlimit(RLIMIT_NOFILE, &rl) == -1) { perror("setrlimit"); @@ -182,7 +201,16 @@ main(int argc, char **argv) exit(1); } - event_init(); + if (method != NULL) { + cfg = event_config_new(); + methods = event_get_supported_methods(); + for (i = 0; methods[i] != NULL; ++i) + if (strcmp(methods[i], method)) + event_config_avoid_method(cfg, methods[i]); + base = event_base_new_with_config(cfg); + event_config_free(cfg); + } else + base = event_base_new(); for (cp = pipes, i = 0; i < num_pipes; i++, cp += 2) { #ifdef USE_PIPES diff --git a/test/bench_cascade.c b/test/bench_cascade.c index 2d85cc1f1038..29a3203ecdd6 100644 --- a/test/bench_cascade.c +++ b/test/bench_cascade.c @@ -35,7 +35,8 @@ #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include <windows.h> -#else +#include <getopt.h> +#else /* _WIN32 */ #include <sys/socket.h> #include <sys/resource.h> #endif @@ -48,7 +49,6 @@ #include <unistd.h> #endif #include <errno.h> -#include <getopt.h> #include <event.h> #include <evutil.h> @@ -139,7 +139,7 @@ run_once(int num_pipes) int main(int argc, char **argv) { -#ifdef HAVE_SETRLIMIT +#ifdef EVENT__HAVE_SETRLIMIT struct rlimit rl; #endif int i, c; @@ -162,7 +162,7 @@ main(int argc, char **argv) } } -#ifdef HAVE_SETRLIMIT +#ifdef EVENT__HAVE_SETRLIMIT rl.rlim_cur = rl.rlim_max = num_pipes * 2 + 50; if (setrlimit(RLIMIT_NOFILE, &rl) == -1) { perror("setrlimit"); diff --git a/test/bench_http.c b/test/bench_http.c index d20d3bc79010..80377ee60583 100644 --- a/test/bench_http.c +++ b/test/bench_http.c @@ -101,6 +101,9 @@ main(int argc, char **argv) return (1); #endif + setbuf(stdout, NULL); + setbuf(stderr, NULL); + for (i = 1; i < argc; ++i) { if (*argv[i] != '-') continue; diff --git a/test/bench_httpclient.c b/test/bench_httpclient.c index e15929519dcc..7c7ee470a307 100644 --- a/test/bench_httpclient.c +++ b/test/bench_httpclient.c @@ -113,13 +113,13 @@ errorcb(struct bufferevent *b, short what, void *arg) static void frob_socket(evutil_socket_t sock) { -#ifdef HAVE_SO_LINGER +#ifdef EVENT__HAVE_STRUCT_LINGER struct linger l; #endif int one = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&one, sizeof(one))<0) perror("setsockopt(SO_REUSEADDR)"); -#ifdef HAVE_SO_LINGER +#ifdef EVENT__HAVE_STRUCT_LINGER l.l_onoff = 1; l.l_linger = 0; if (setsockopt(sock, SOL_SOCKET, SO_LINGER, (void*)&l, sizeof(l))<0) diff --git a/test/check-dumpevents.py b/test/check-dumpevents.py index 16fe9bc92fdf..3e1df30c4f40 100755 --- a/test/check-dumpevents.py +++ b/test/check-dumpevents.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/env python # # Post-process the output of test-dumpevents and check it for correctness. # @@ -15,12 +15,12 @@ try: got_inserted_pos = text.index("Inserted events:\n") got_active_pos = text.index("Active events:\n") except ValueError: - print >>sys.stderr, "Missing expected dividing line in dumpevents output" + sys.stderr.write("Missing expected dividing line in dumpevents output") sys.exit(1) if not (expect_inserted_pos < expect_active_pos < got_inserted_pos < got_active_pos): - print >>sys.stderr, "Sections out of order in dumpevents output" + sys.stderr.write("Sections out of order in dumpevents output") sys.exit(1) now,T= text[1].split() @@ -45,10 +45,10 @@ cleaned_inserted = set( pat.sub(replace_time, s) for s in got_inserted if "Internal" not in s) if cleaned_inserted != want_inserted: - print >>sys.stderr, "Inserted event lists were not as expected!" + sys.stderr.write("Inserted event lists were not as expected!") sys.exit(1) if set(got_active) != set(want_active): - print >>sys.stderr, "Active event lists were not as expected!" + sys.stderr.write("Active event lists were not as expected!") sys.exit(1) diff --git a/test/include.am b/test/include.am index eea249f80229..043752471aed 100644 --- a/test/include.am +++ b/test/include.am @@ -12,6 +12,7 @@ EXTRA_DIST+= \ test/regress.gen.h \ test/regress.rpc \ test/rpcgen_wrapper.sh \ + test/print-winsock-errors.c \ test/test.sh TESTPROGRAMS = \ @@ -56,26 +57,26 @@ TESTS = \ LOG_COMPILER = true TESTS_COMPILER = true -test_runner_epoll: test/test.sh - test/test.sh -b EPOLL -test_runner_select: test/test.sh - test/test.sh -b SELECT -test_runner_kqueue: test/test.sh - test/test.sh -b KQUEUE -test_runner_evport: test/test.sh - test/test.sh -b EVPORT -test_runner_devpoll: test/test.sh - test/test.sh -b DEVPOLL -test_runner_poll: test/test.sh - test/test.sh -b POLL -test_runner_win32: test/test.sh - test/test.sh -b WIN32 -test_runner_timerfd: test/test.sh - test/test.sh -b "" -t -test_runner_changelist: test/test.sh - test/test.sh -b "" -c -test_runner_timerfd_changelist: test/test.sh - test/test.sh -b "" -T +test_runner_epoll: $(top_srcdir)/test/test.sh + $(top_srcdir)/test/test.sh -b EPOLL +test_runner_select: $(top_srcdir)/test/test.sh + $(top_srcdir)/test/test.sh -b SELECT +test_runner_kqueue: $(top_srcdir)/test/test.sh + $(top_srcdir)/test/test.sh -b KQUEUE +test_runner_evport: $(top_srcdir)/test/test.sh + $(top_srcdir)/test/test.sh -b EVPORT +test_runner_devpoll: $(top_srcdir)/test/test.sh + $(top_srcdir)/test/test.sh -b DEVPOLL +test_runner_poll: $(top_srcdir)/test/test.sh + $(top_srcdir)/test/test.sh -b POLL +test_runner_win32: $(top_srcdir)/test/test.sh + $(top_srcdir)/test/test.sh -b WIN32 +test_runner_timerfd: $(top_srcdir)/test/test.sh + $(top_srcdir)/test/test.sh -b "" -t +test_runner_changelist: $(top_srcdir)/test/test.sh + $(top_srcdir)/test/test.sh -b "" -c +test_runner_timerfd_changelist: $(top_srcdir)/test/test.sh + $(top_srcdir)/test/test.sh -b "" -T DISTCLEANFILES += test/regress.gen.c test/regress.gen.h @@ -139,7 +140,7 @@ if BUILD_WIN32 test_regress_SOURCES += test/regress_iocp.c endif -test_regress_LDADD = $(LIBEVENT_GC_SECTIONS) libevent.la $(PTHREAD_LIBS) $(ZLIB_LIBS) +test_regress_LDADD = $(LIBEVENT_GC_SECTIONS) libevent_core.la libevent_extra.la $(PTHREAD_LIBS) $(ZLIB_LIBS) test_regress_CPPFLAGS = $(AM_CPPFLAGS) $(PTHREAD_CFLAGS) $(ZLIB_CFLAGS) -Itest test_regress_LDFLAGS = $(PTHREAD_CFLAGS) diff --git a/test/print-winsock-errors.c b/test/print-winsock-errors.c new file mode 100644 index 000000000000..64d6b0e70e4d --- /dev/null +++ b/test/print-winsock-errors.c @@ -0,0 +1,86 @@ +#include <winsock2.h> +#include <windows.h> + +#include <stdlib.h> +#include <stdio.h> + +#include "event2/event.h" +#include "event2/util.h" +#include "event2/thread.h" + +#define E(x) printf (#x " -> \"%s\"\n", evutil_socket_error_to_string (x)); + +int main (int argc, char **argv) +{ + int i, j; + const char *s1, *s2; + +#ifdef EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED + evthread_use_windows_threads (); +#endif + + s1 = evutil_socket_error_to_string (WSAEINTR); + + for (i = 0; i < 3; i++) { + printf ("\niteration %d:\n\n", i); + E(WSAEINTR); + E(WSAEACCES); + E(WSAEFAULT); + E(WSAEINVAL); + E(WSAEMFILE); + E(WSAEWOULDBLOCK); + E(WSAEINPROGRESS); + E(WSAEALREADY); + E(WSAENOTSOCK); + E(WSAEDESTADDRREQ); + E(WSAEMSGSIZE); + E(WSAEPROTOTYPE); + E(WSAENOPROTOOPT); + E(WSAEPROTONOSUPPORT); + E(WSAESOCKTNOSUPPORT); + E(WSAEOPNOTSUPP); + E(WSAEPFNOSUPPORT); + E(WSAEAFNOSUPPORT); + E(WSAEADDRINUSE); + E(WSAEADDRNOTAVAIL); + E(WSAENETDOWN); + E(WSAENETUNREACH); + E(WSAENETRESET); + E(WSAECONNABORTED); + E(WSAECONNRESET); + E(WSAENOBUFS); + E(WSAEISCONN); + E(WSAENOTCONN); + E(WSAESHUTDOWN); + E(WSAETIMEDOUT); + E(WSAECONNREFUSED); + E(WSAEHOSTDOWN); + E(WSAEHOSTUNREACH); + E(WSAEPROCLIM); + E(WSASYSNOTREADY); + E(WSAVERNOTSUPPORTED); + E(WSANOTINITIALISED); + E(WSAEDISCON); + E(WSATYPE_NOT_FOUND); + E(WSAHOST_NOT_FOUND); + E(WSATRY_AGAIN); + E(WSANO_RECOVERY); + E(WSANO_DATA); + E(0xdeadbeef); /* test the case where no message is available */ + + /* fill up the hash table a bit to make sure it grows properly */ + for (j = 0; j < 50; j++) { + int err; + evutil_secure_rng_get_bytes(&err, sizeof(err)); + evutil_socket_error_to_string(err); + } + } + + s2 = evutil_socket_error_to_string (WSAEINTR); + if (s1 != s2) + printf ("caching failed!\n"); + + libevent_global_shutdown (); + + return EXIT_SUCCESS; +} diff --git a/test/regress.c b/test/regress.c index d8a6b9b85485..08c30fab9b9b 100644 --- a/test/regress.c +++ b/test/regress.c @@ -31,10 +31,6 @@ #include <windows.h> #endif -#ifdef EVENT__HAVE_PTHREADS -#include <pthread.h> -#endif - #include "event2/event-config.h" #include <sys/types.h> @@ -46,6 +42,7 @@ #ifndef _WIN32 #include <sys/socket.h> #include <sys/wait.h> +#include <limits.h> #include <signal.h> #include <unistd.h> #include <netdb.h> @@ -72,6 +69,7 @@ #include "time-internal.h" #include "regress.h" +#include "regress_thread.h" #ifndef _WIN32 #include "regress.gen.h" @@ -389,7 +387,7 @@ record_event_cb(evutil_socket_t s, short what, void *ptr) } static void -test_simpleclose(void *ptr) +test_simpleclose_rw(void *ptr) { /* Test that a close of FD is detected as a read and as a write. */ struct event_base *base = event_base_new(); @@ -471,6 +469,56 @@ end: event_base_free(base); } +static void +test_simpleclose(void *ptr) +{ + struct basic_test_data *data = ptr; + struct event_base *base = data->base; + evutil_socket_t *pair = data->pair; + const char *flags = (const char *)data->setup_data; + int et = !!strstr(flags, "ET"); + int persist = !!strstr(flags, "persist"); + short events = EV_CLOSED | (et ? EV_ET : 0) | (persist ? EV_PERSIST : 0); + struct event *ev = NULL; + short got_event; + + if (!(event_base_get_features(data->base) & EV_FEATURE_EARLY_CLOSE)) + tt_skip(); + + /* XXX: should this code moved to regress_et.c ? */ + if (et && !(event_base_get_features(data->base) & EV_FEATURE_ET)) + tt_skip(); + + ev = event_new(base, pair[0], events, record_event_cb, &got_event); + tt_assert(ev); + tt_assert(!event_add(ev, NULL)); + + got_event = 0; + if (strstr(flags, "close")) { + tt_assert(!evutil_closesocket(pair[1])); + /* avoid closing in setup routines */ + pair[1] = -1; + } else if (strstr(flags, "shutdown")) { + tt_assert(!shutdown(pair[1], EVUTIL_SHUT_WR)); + } else { + tt_abort_msg("unknown flags"); + } + + /* w/o edge-triggerd but w/ persist it will not stop */ + if (!et && persist) { + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 10000; + tt_assert(!event_base_loopexit(base, &tv)); + } + + tt_int_op(event_base_loop(base, EVLOOP_NONBLOCK), ==, !persist); + tt_int_op(got_event, ==, (events & ~EV_PERSIST)); + +end: + if (ev) + event_free(ev); +} static void test_multiple(void) @@ -848,18 +896,36 @@ simple_child_read_cb(evutil_socket_t fd, short event, void *arg) called++; } + +#define TEST_FORK_EXIT_SUCCESS 76 +static void fork_wait_check(int pid) +{ + int status; + + TT_BLATHER(("Before waitpid")); + +#ifdef WNOWAIT + if ((waitpid(pid, &status, WNOWAIT) == -1 && errno == EINVAL) && +#else + if ( +#endif + waitpid(pid, &status, 0) == -1) { + perror("waitpid"); + exit(1); + } + TT_BLATHER(("After waitpid")); + + if (WEXITSTATUS(status) != TEST_FORK_EXIT_SUCCESS) { + fprintf(stdout, "FAILED (exit): %d\n", WEXITSTATUS(status)); + exit(1); + } +} static void test_fork(void) { char c; - int status; struct event ev, sig_ev, usr_ev, existing_ev; pid_t pid; - int wait_flags = 0; - -#ifdef EVENT__HAVE_WAITPID_WITH_WNOWAIT - wait_flags |= WNOWAIT; -#endif setup_test("After fork: "); @@ -910,8 +976,8 @@ test_fork(void) evsignal_set(&usr_ev, SIGUSR1, fork_signal_cb, &usr_ev); evsignal_add(&usr_ev, NULL); - raise(SIGUSR1); - raise(SIGUSR2); + kill(getpid(), SIGUSR1); + kill(getpid(), SIGUSR2); called = 0; @@ -922,7 +988,7 @@ test_fork(void) /* we do not send an EOF; simple_read_cb requires an EOF * to set test_ok. we just verify that the callback was * called. */ - exit(test_ok != 0 || called != 2 ? -2 : 76); + exit(test_ok != 0 || called != 2 ? -2 : TEST_FORK_EXIT_SUCCESS); } /** wait until client read first message */ @@ -933,17 +999,7 @@ test_fork(void) tt_fail_perror("write"); } - TT_BLATHER(("Before waitpid")); - if (waitpid(pid, &status, wait_flags) == -1) { - perror("waitpid"); - exit(1); - } - TT_BLATHER(("After waitpid")); - - if (WEXITSTATUS(status) != 76) { - fprintf(stdout, "FAILED (exit): %d\n", WEXITSTATUS(status)); - exit(1); - } + fork_wait_check(pid); /* test that the current event loop still works */ if (write(pair[0], TEST1, strlen(TEST1)+1) < 0) { @@ -954,8 +1010,8 @@ test_fork(void) evsignal_set(&usr_ev, SIGUSR1, fork_signal_cb, &usr_ev); evsignal_add(&usr_ev, NULL); - raise(SIGUSR1); - raise(SIGUSR2); + kill(getpid(), SIGUSR1); + kill(getpid(), SIGUSR2); event_dispatch(); @@ -970,7 +1026,7 @@ test_fork(void) evutil_closesocket(child_pair[1]); } -#ifdef EVENT__HAVE_PTHREADS +#ifdef EVTHREAD_USE_PTHREADS_IMPLEMENTED static void* del_wait_thread(void *arg) { struct timeval tv_start, tv_end; @@ -989,23 +1045,23 @@ static void del_wait_cb(evutil_socket_t fd, short event, void *arg) { struct timeval delay = { 0, 300*1000 }; - TT_BLATHER(("Sleeping")); + TT_BLATHER(("Sleeping: %i", test_ok)); evutil_usleep_(&delay); - test_ok = 1; + ++test_ok; } static void test_del_wait(void) { struct event ev; - pthread_t thread; + THREAD_T thread; setup_test("event_del will wait: "); - event_set(&ev, pair[1], EV_READ, del_wait_cb, &ev); + event_set(&ev, pair[1], EV_READ|EV_PERSIST, del_wait_cb, &ev); event_add(&ev, NULL); - pthread_create(&thread, NULL, del_wait_thread, NULL); + THREAD_START(thread, del_wait_thread, NULL); if (write(pair[0], TEST1, strlen(TEST1)+1) < 0) { tt_fail_perror("write"); @@ -1024,11 +1080,41 @@ test_del_wait(void) test_timeval_diff_eq(&tv_start, &tv_end, 270); } - pthread_join(thread, NULL); + THREAD_JOIN(thread); + + tt_int_op(test_ok, ==, 1); end: ; } + +static void null_cb(evutil_socket_t fd, short what, void *arg) {} +static void* test_del_notify_thread(void *arg) +{ + event_dispatch(); + return NULL; +} +static void +test_del_notify(void) +{ + struct event ev; + THREAD_T thread; + + test_ok = 1; + + event_set(&ev, -1, EV_READ, null_cb, &ev); + event_add(&ev, NULL); + + THREAD_START(thread, test_del_notify_thread, NULL); + + { + struct timeval delay = { 0, 1000 }; + evutil_usleep_(&delay); + } + + event_del(&ev); + THREAD_JOIN(thread); +} #endif static void @@ -1127,7 +1213,7 @@ test_immediatesignal(void) test_ok = 0; evsignal_set(&ev, SIGUSR1, signal_cb, &ev); evsignal_add(&ev, NULL); - raise(SIGUSR1); + kill(getpid(), SIGUSR1); event_loop(EVLOOP_NONBLOCK); evsignal_del(&ev); cleanup_test(); @@ -1200,7 +1286,7 @@ test_signal_switchbase(void) test_ok = 0; /* can handle signal before loop is called */ - raise(SIGUSR1); + kill(getpid(), SIGUSR1); event_base_loop(base2, EVLOOP_NONBLOCK); if (is_kqueue) { if (!test_ok) @@ -1213,7 +1299,7 @@ test_signal_switchbase(void) /* set base1 to handle signals */ event_base_loop(base1, EVLOOP_NONBLOCK); - raise(SIGUSR1); + kill(getpid(), SIGUSR1); event_base_loop(base1, EVLOOP_NONBLOCK); event_base_loop(base2, EVLOOP_NONBLOCK); } @@ -1242,7 +1328,7 @@ test_signal_assert(void) */ evsignal_del(&ev); - raise(SIGCONT); + kill(getpid(), SIGCONT); #if 0 /* only way to verify we were in evsig_handler() */ /* XXXX Now there's no longer a good way. */ @@ -1286,7 +1372,7 @@ test_signal_restore(void) evsignal_add(&ev, NULL); evsignal_del(&ev); - raise(SIGUSR1); + kill(getpid(), SIGUSR1); /* 1 == signal_cb, 2 == signal_cb_sa, we want our previous handler */ if (test_ok != 2) test_ok = 0; @@ -1301,7 +1387,7 @@ signal_cb_swp(int sig, short event, void *arg) { called++; if (called < 5) - raise(sig); + kill(getpid(), sig); else event_loopexit(NULL); } @@ -1313,7 +1399,7 @@ timeout_cb_swp(evutil_socket_t fd, short event, void *arg) called = 0; evtimer_add((struct event *)arg, &tv); - raise(SIGUSR1); + kill(getpid(), SIGUSR1); return; } test_ok = 0; @@ -1351,18 +1437,14 @@ test_free_active_base(void *ptr) struct event ev1; base1 = event_init(); - if (base1) { - event_assign(&ev1, base1, data->pair[1], EV_READ, - dummy_read_cb, NULL); - event_add(&ev1, NULL); - event_base_free(base1); /* should not crash */ - } else { - tt_fail_msg("failed to create event_base for test"); - } + tt_assert(base1); + event_assign(&ev1, base1, data->pair[1], EV_READ, dummy_read_cb, NULL); + event_add(&ev1, NULL); + event_base_free(base1); /* should not crash */ base1 = event_init(); tt_assert(base1); - event_assign(&ev1, base1, 0, 0, dummy_read_cb, NULL); + event_assign(&ev1, base1, data->pair[0], 0, dummy_read_cb, NULL); event_active(&ev1, EV_READ, 1); event_base_free(base1); end: @@ -1840,7 +1922,8 @@ static void send_a_byte_cb(evutil_socket_t fd, short what, void *arg) { evutil_socket_t *sockp = arg; (void) fd; (void) what; - (void) write(*sockp, "A", 1); + if (write(*sockp, "A", 1) < 0) + tt_fail_perror("write"); } struct read_not_timeout_param { @@ -2070,60 +2153,48 @@ re_add_read_cb(evutil_socket_t fd, short event, void *arg) if (n_read < 0) { tt_fail_perror("read"); event_base_loopbreak(event_get_base(ev_other)); - return; } else { event_add(ev_other, NULL); ++test_ok; } } - static void -test_nonpersist_readd(void) +test_nonpersist_readd(void *_data) { struct event ev1, ev2; + struct basic_test_data *data = _data; - setup_test("Re-add nonpersistent events: "); - event_set(&ev1, pair[0], EV_READ, re_add_read_cb, &ev2); - event_set(&ev2, pair[1], EV_READ, re_add_read_cb, &ev1); + memset(&ev1, 0, sizeof(ev1)); + memset(&ev2, 0, sizeof(ev2)); - if (write(pair[0], "Hello", 5) < 0) { - tt_fail_perror("write(pair[0])"); - } + tt_assert(!event_assign(&ev1, data->base, data->pair[0], EV_READ, re_add_read_cb, &ev2)); + tt_assert(!event_assign(&ev2, data->base, data->pair[1], EV_READ, re_add_read_cb, &ev1)); - if (write(pair[1], "Hello", 5) < 0) { - tt_fail_perror("write(pair[1])\n"); - } + tt_int_op(write(data->pair[0], "Hello", 5), ==, 5); + tt_int_op(write(data->pair[1], "Hello", 5), ==, 5); + + tt_int_op(event_add(&ev1, NULL), ==, 0); + tt_int_op(event_add(&ev2, NULL), ==, 0); + tt_int_op(event_base_loop(data->base, EVLOOP_ONCE), ==, 0); + tt_int_op(test_ok, ==, 2); - if (event_add(&ev1, NULL) == -1 || - event_add(&ev2, NULL) == -1) { - test_ok = 0; - } - if (test_ok != 0) - exit(1); - event_loop(EVLOOP_ONCE); - if (test_ok != 2) - exit(1); /* At this point, we executed both callbacks. Whichever one got * called first added the second, but the second then immediately got * deleted before its callback was called. At this point, though, it * re-added the first. */ - if (!readd_test_event_last_added) { - test_ok = 0; - } else if (readd_test_event_last_added == &ev1) { - if (!event_pending(&ev1, EV_READ, NULL) || - event_pending(&ev2, EV_READ, NULL)) - test_ok = 0; + tt_assert(readd_test_event_last_added); + if (readd_test_event_last_added == &ev1) { + tt_assert(event_pending(&ev1, EV_READ, NULL) && !event_pending(&ev2, EV_READ, NULL)); } else { - if (event_pending(&ev1, EV_READ, NULL) || - !event_pending(&ev2, EV_READ, NULL)) - test_ok = 0; + tt_assert(event_pending(&ev2, EV_READ, NULL) && !event_pending(&ev1, EV_READ, NULL)); } - event_del(&ev1); - event_del(&ev2); - - cleanup_test(); +end: + if (event_initialized(&ev1)) + event_del(&ev1); + if (event_initialized(&ev2)) + event_del(&ev2); } struct test_pri_event { @@ -3041,6 +3112,7 @@ test_many_events(void *arg) * instance of that. */ sock[i] = socket(AF_INET, SOCK_DGRAM, 0); tt_assert(sock[i] >= 0); + tt_assert(!evutil_make_socket_nonblocking(sock[i])); called[i] = 0; ev[i] = event_new(base, sock[i], EV_WRITE|evflags, many_event_cb, &called[i]); @@ -3094,7 +3166,7 @@ test_get_assignment(void *arg) event_get_assignment(ev1, &b, &s, &what, &cb, &cb_arg); tt_ptr_op(b, ==, base); - tt_int_op(s, ==, data->pair[1]); + tt_fd_op(s, ==, data->pair[1]); tt_int_op(what, ==, EV_READ); tt_ptr_op(cb, ==, dummy_read_cb); tt_ptr_op(cb_arg, ==, str); @@ -3280,6 +3352,46 @@ tabf_cb(evutil_socket_t fd, short what, void *arg) } static void +test_evmap_invalid_slots(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct event *ev1 = NULL, *ev2 = NULL; + int e1, e2; +#ifndef _WIN32 + struct event *ev3 = NULL, *ev4 = NULL; + int e3, e4; +#endif + + ev1 = evsignal_new(base, -1, dummy_read_cb, (void *)base); + ev2 = evsignal_new(base, NSIG, dummy_read_cb, (void *)base); + tt_assert(ev1); + tt_assert(ev2); + e1 = event_add(ev1, NULL); + e2 = event_add(ev2, NULL); + tt_int_op(e1, !=, 0); + tt_int_op(e2, !=, 0); +#ifndef _WIN32 + ev3 = event_new(base, INT_MAX, EV_READ, dummy_read_cb, (void *)base); + ev4 = event_new(base, INT_MAX / 2, EV_READ, dummy_read_cb, (void *)base); + tt_assert(ev3); + tt_assert(ev4); + e3 = event_add(ev3, NULL); + e4 = event_add(ev4, NULL); + tt_int_op(e3, !=, 0); + tt_int_op(e4, !=, 0); +#endif + +end: + event_free(ev1); + event_free(ev2); +#ifndef _WIN32 + event_free(ev3); + event_free(ev4); +#endif +} + +static void test_active_by_fd(void *arg) { struct basic_test_data *data = arg; @@ -3330,6 +3442,7 @@ test_active_by_fd(void *arg) /* Trigger 2, 3, 4 */ event_base_active_by_fd(base, data->pair[0], EV_WRITE); event_base_active_by_fd(base, data->pair[1], EV_READ); + event_base_active_by_fd(base, data->pair[1], EV_TIMEOUT); #ifndef _WIN32 event_base_active_by_signal(base, SIGHUP); #endif @@ -3342,7 +3455,7 @@ test_active_by_fd(void *arg) tt_int_op(e2, ==, EV_WRITE | 0x10000); tt_int_op(e3, ==, EV_READ | 0x10000); /* Mask out EV_WRITE here, since it could be genuinely writeable. */ - tt_int_op((e4 & ~EV_WRITE), ==, EV_READ | 0x10000); + tt_int_op((e4 & ~EV_WRITE), ==, EV_READ | EV_TIMEOUT | 0x10000); #ifndef _WIN32 tt_int_op(es, ==, EV_SIGNAL | 0x10000); #endif @@ -3377,17 +3490,18 @@ struct testcase_t main_testcases[] = { BASIC(event_assign_selfarg, TT_FORK|TT_NEED_BASE), BASIC(event_base_get_num_events, TT_FORK|TT_NEED_BASE), BASIC(event_base_get_max_events, TT_FORK|TT_NEED_BASE), + BASIC(evmap_invalid_slots, TT_FORK|TT_NEED_BASE), BASIC(bad_assign, TT_FORK|TT_NEED_BASE|TT_NO_LOGS), BASIC(bad_reentrant, TT_FORK|TT_NEED_BASE|TT_NO_LOGS), - BASIC(active_later, TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR), + BASIC(active_later, TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR|TT_RETRIABLE), BASIC(event_remove_timeout, TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR), /* These are still using the old API */ LEGACY(persistent_timeout, TT_FORK|TT_NEED_BASE), { "persistent_timeout_jump", test_persistent_timeout_jump, TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, { "persistent_active_timeout", test_persistent_active_timeout, - TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + TT_FORK|TT_NEED_BASE|TT_RETRIABLE, &basic_setup, NULL }, LEGACY(priorities, TT_FORK|TT_NEED_BASE), BASIC(priority_active_inversion, TT_FORK|TT_NEED_BASE), { "common_timeout", test_common_timeout, TT_FORK|TT_NEED_BASE, @@ -3397,8 +3511,35 @@ struct testcase_t main_testcases[] = { LEGACY(simpleread, TT_ISOLATED), LEGACY(simpleread_multiple, TT_ISOLATED), LEGACY(simplewrite, TT_ISOLATED), - { "simpleclose", test_simpleclose, TT_FORK, &basic_setup, - NULL }, + { "simpleclose_rw", test_simpleclose_rw, TT_FORK, &basic_setup, NULL }, + /* simpleclose */ + { "simpleclose_close", test_simpleclose, + TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_BASE, + &basic_setup, (void *)"close" }, + { "simpleclose_shutdown", test_simpleclose, + TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_BASE, + &basic_setup, (void *)"shutdown" }, + /* simpleclose_*_persist */ + { "simpleclose_close_persist", test_simpleclose, + TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_BASE, + &basic_setup, (void *)"close_persist" }, + { "simpleclose_shutdown_persist", test_simpleclose, + TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_BASE, + &basic_setup, (void *)"shutdown_persist" }, + /* simpleclose_*_et */ + { "simpleclose_close_et", test_simpleclose, + TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_BASE, + &basic_setup, (void *)"close_ET" }, + { "simpleclose_shutdown_et", test_simpleclose, + TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_BASE, + &basic_setup, (void *)"shutdown_ET" }, + /* simpleclose_*_persist_et */ + { "simpleclose_close_persist_et", test_simpleclose, + TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_BASE, + &basic_setup, (void *)"close_persist_ET" }, + { "simpleclose_shutdown_persist_et", test_simpleclose, + TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_BASE, + &basic_setup, (void *)"shutdown_persist_ET" }, LEGACY(multiple, TT_ISOLATED), LEGACY(persistent, TT_ISOLATED), LEGACY(combined, TT_ISOLATED), @@ -3406,7 +3547,7 @@ struct testcase_t main_testcases[] = { LEGACY(loopbreak, TT_ISOLATED), LEGACY(loopexit, TT_ISOLATED), LEGACY(loopexit_multiple, TT_ISOLATED), - LEGACY(nonpersist_readd, TT_ISOLATED), + { "nonpersist_readd", test_nonpersist_readd, TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_BASE, &basic_setup, NULL }, LEGACY(multiple_events_for_same_fd, TT_ISOLATED), LEGACY(want_only_once, TT_ISOLATED), { "event_once", test_event_once, TT_ISOLATED, &basic_setup, NULL }, @@ -3438,9 +3579,10 @@ struct testcase_t main_testcases[] = { #ifndef _WIN32 LEGACY(fork, TT_ISOLATED), #endif -#ifdef EVENT__HAVE_PTHREADS - /** TODO: support win32 */ - LEGACY(del_wait, TT_ISOLATED|TT_NEED_THREADS), + +#ifdef EVTHREAD_USE_PTHREADS_IMPLEMENTED + LEGACY(del_wait, TT_ISOLATED|TT_NEED_THREADS|TT_RETRIABLE), + LEGACY(del_notify, TT_ISOLATED|TT_NEED_THREADS), #endif END_OF_TESTCASES diff --git a/test/regress.gen.c b/test/regress.gen.c index da994a51cb6e..611ec8af6714 100644 --- a/test/regress.gen.c +++ b/test/regress.gen.c @@ -1,5 +1,6 @@ + /* - * Automatically generated from ./test/regress.rpc + * Automatically generated from ../test/regress.rpc * by event_rpcgen.py/0.1. DO NOT EDIT THIS FILE. */ @@ -11,8 +12,14 @@ #include <event2/buffer.h> #include <event2/tag.h> -#if defined(EVENT____func__) && !defined(__func__) -#define __func__ EVENT____func__ +#if defined(EVENT__HAVE___func__) +# ifndef __func__ +# define __func__ __func__ +# endif +#elif defined(EVENT__HAVE___FUNCTION__) +# define __func__ __FUNCTION__ +#else +# define __func__ __FILE__ #endif @@ -21,7 +28,6 @@ void event_warn(const char *fmt, ...); void event_warnx(const char *fmt, ...); - /* * Implementation of msg */ @@ -86,7 +92,8 @@ msg_run_expand_to_hold_more(struct msg *msg) return -1; msg->run_data = new_data; msg->run_num_allocated = tobe_allocated; - return 0;} + return 0; +} struct run* msg_run_add(struct msg *msg) @@ -168,7 +175,7 @@ msg_attack_assign(struct msg *msg, int msg_run_assign(struct msg *msg, int off, - const struct run* value) + const struct run* value) { if (!msg->run_set || off < 0 || off >= msg->run_length) return (-1); @@ -188,7 +195,8 @@ msg_run_assign(struct msg *msg, int off, had_error = 1; goto done; } - done:if (tmp != NULL) + done: + if (tmp != NULL) evbuffer_free(tmp); if (had_error) { run_clear(msg->run_data[off]); @@ -295,7 +303,7 @@ msg_free(struct msg *tmp) } void -msg_marshal(struct evbuffer *evbuf, const struct msg *tmp){ +msg_marshal(struct evbuffer *evbuf, const struct msg *tmp) { evtag_marshal_string(evbuf, MSG_FROM_NAME, tmp->from_name_data); evtag_marshal_string(evbuf, MSG_TO_NAME, tmp->to_name_data); if (tmp->attack_set) { @@ -312,7 +320,7 @@ msg_marshal(struct evbuffer *evbuf, const struct msg *tmp){ } int -msg_unmarshal(struct msg *tmp, struct evbuffer *evbuf) +msg_unmarshal(struct msg *tmp, struct evbuffer *evbuf) { ev_uint32_t tag; while (evbuffer_get_length(evbuf) > 0) { @@ -321,7 +329,6 @@ msg_unmarshal(struct msg *tmp, struct evbuffer *evbuf) switch (tag) { case MSG_FROM_NAME: - if (tmp->from_name_set) return (-1); if (evtag_unmarshal_string(evbuf, MSG_FROM_NAME, &tmp->from_name_data) == -1) { @@ -330,9 +337,7 @@ msg_unmarshal(struct msg *tmp, struct evbuffer *evbuf) } tmp->from_name_set = 1; break; - case MSG_TO_NAME: - if (tmp->to_name_set) return (-1); if (evtag_unmarshal_string(evbuf, MSG_TO_NAME, &tmp->to_name_data) == -1) { @@ -341,23 +346,20 @@ msg_unmarshal(struct msg *tmp, struct evbuffer *evbuf) } tmp->to_name_set = 1; break; - case MSG_ATTACK: - if (tmp->attack_set) return (-1); tmp->attack_data = kill_new(); if (tmp->attack_data == NULL) return (-1); - if (evtag_unmarshal_kill(evbuf, MSG_ATTACK, tmp->attack_data) == -1) { + if (evtag_unmarshal_kill(evbuf, MSG_ATTACK, + tmp->attack_data) == -1) { event_warnx("%s: failed to unmarshal attack", __func__); return (-1); } tmp->attack_set = 1; break; - case MSG_RUN: - if (tmp->run_length >= tmp->run_num_allocated && msg_run_expand_to_hold_more(tmp) < 0) { puts("HEY NOW"); @@ -366,14 +368,14 @@ msg_unmarshal(struct msg *tmp, struct evbuffer *evbuf) tmp->run_data[tmp->run_length] = run_new(); if (tmp->run_data[tmp->run_length] == NULL) return (-1); - if (evtag_unmarshal_run(evbuf, MSG_RUN, tmp->run_data[tmp->run_length]) == -1) { + if (evtag_unmarshal_run(evbuf, MSG_RUN, + tmp->run_data[tmp->run_length]) == -1) { event_warnx("%s: failed to unmarshal run", __func__); return (-1); } ++tmp->run_length; tmp->run_set = 1; break; - default: return -1; } @@ -404,7 +406,8 @@ msg_complete(struct msg *msg) } int -evtag_unmarshal_msg(struct evbuffer *evbuf, ev_uint32_t need_tag, struct msg *msg) +evtag_unmarshal_msg(struct evbuffer *evbuf, ev_uint32_t need_tag, + struct msg *msg) { ev_uint32_t tag; int res = -1; @@ -425,13 +428,14 @@ evtag_unmarshal_msg(struct evbuffer *evbuf, ev_uint32_t need_tag, struct msg *ms } void -evtag_marshal_msg(struct evbuffer *evbuf, ev_uint32_t tag, const struct msg *msg) +evtag_marshal_msg(struct evbuffer *evbuf, ev_uint32_t tag, + const struct msg *msg) { struct evbuffer *buf_ = evbuffer_new(); assert(buf_ != NULL); msg_marshal(buf_, msg); evtag_marshal_buffer(evbuf, tag, buf_); - evbuffer_free(buf_); + evbuffer_free(buf_); } /* @@ -492,7 +496,8 @@ kill_how_often_expand_to_hold_more(struct kill *msg) return -1; msg->how_often_data = new_data; msg->how_often_num_allocated = tobe_allocated; - return 0;} + return 0; +} ev_uint32_t * kill_how_often_add(struct kill *msg, const ev_uint32_t value) @@ -535,7 +540,7 @@ kill_action_assign(struct kill *msg, int kill_how_often_assign(struct kill *msg, int off, - const ev_uint32_t value) + const ev_uint32_t value) { if (!msg->how_often_set || off < 0 || off >= msg->how_often_length) return (-1); @@ -615,7 +620,7 @@ kill_free(struct kill *tmp) } void -kill_marshal(struct evbuffer *evbuf, const struct kill *tmp){ +kill_marshal(struct evbuffer *evbuf, const struct kill *tmp) { evtag_marshal_string(evbuf, KILL_WEAPON, tmp->weapon_data); evtag_marshal_string(evbuf, KILL_ACTION, tmp->action_data); if (tmp->how_often_set) { @@ -629,7 +634,7 @@ kill_marshal(struct evbuffer *evbuf, const struct kill *tmp){ } int -kill_unmarshal(struct kill *tmp, struct evbuffer *evbuf) +kill_unmarshal(struct kill *tmp, struct evbuffer *evbuf) { ev_uint32_t tag; while (evbuffer_get_length(evbuf) > 0) { @@ -638,7 +643,6 @@ kill_unmarshal(struct kill *tmp, struct evbuffer *evbuf) switch (tag) { case KILL_WEAPON: - if (tmp->weapon_set) return (-1); if (evtag_unmarshal_string(evbuf, KILL_WEAPON, &tmp->weapon_data) == -1) { @@ -647,9 +651,7 @@ kill_unmarshal(struct kill *tmp, struct evbuffer *evbuf) } tmp->weapon_set = 1; break; - case KILL_ACTION: - if (tmp->action_set) return (-1); if (evtag_unmarshal_string(evbuf, KILL_ACTION, &tmp->action_data) == -1) { @@ -658,9 +660,7 @@ kill_unmarshal(struct kill *tmp, struct evbuffer *evbuf) } tmp->action_set = 1; break; - case KILL_HOW_OFTEN: - if (tmp->how_often_length >= tmp->how_often_num_allocated && kill_how_often_expand_to_hold_more(tmp) < 0) { puts("HEY NOW"); @@ -673,7 +673,6 @@ kill_unmarshal(struct kill *tmp, struct evbuffer *evbuf) ++tmp->how_often_length; tmp->how_often_set = 1; break; - default: return -1; } @@ -695,7 +694,8 @@ kill_complete(struct kill *msg) } int -evtag_unmarshal_kill(struct evbuffer *evbuf, ev_uint32_t need_tag, struct kill *msg) +evtag_unmarshal_kill(struct evbuffer *evbuf, ev_uint32_t need_tag, + struct kill *msg) { ev_uint32_t tag; int res = -1; @@ -716,13 +716,14 @@ evtag_unmarshal_kill(struct evbuffer *evbuf, ev_uint32_t need_tag, struct kill * } void -evtag_marshal_kill(struct evbuffer *evbuf, ev_uint32_t tag, const struct kill *msg) +evtag_marshal_kill(struct evbuffer *evbuf, ev_uint32_t tag, + const struct kill *msg) { struct evbuffer *buf_ = evbuffer_new(); assert(buf_ != NULL); kill_marshal(buf_, msg); evtag_marshal_buffer(evbuf, tag, buf_); - evbuffer_free(buf_); + evbuffer_free(buf_); } /* @@ -803,7 +804,8 @@ run_notes_expand_to_hold_more(struct run *msg) return -1; msg->notes_data = new_data; msg->notes_num_allocated = tobe_allocated; - return 0;} + return 0; +} char * * run_notes_add(struct run *msg, const char * value) @@ -840,7 +842,8 @@ run_other_numbers_expand_to_hold_more(struct run *msg) return -1; msg->other_numbers_data = new_data; msg->other_numbers_num_allocated = tobe_allocated; - return 0;} + return 0; +} ev_uint32_t * run_other_numbers_add(struct run *msg, const ev_uint32_t value) @@ -893,7 +896,7 @@ run_fixed_bytes_assign(struct run *msg, const ev_uint8_t *value) int run_notes_assign(struct run *msg, int off, - const char * value) + const char * value) { if (!msg->notes_set || off < 0 || off >= msg->notes_length) return (-1); @@ -920,7 +923,7 @@ run_large_number_assign(struct run *msg, const ev_uint64_t value) int run_other_numbers_assign(struct run *msg, int off, - const ev_uint32_t value) + const ev_uint32_t value) { if (!msg->other_numbers_set || off < 0 || off >= msg->other_numbers_length) return (-1); @@ -1056,7 +1059,7 @@ run_free(struct run *tmp) } void -run_marshal(struct evbuffer *evbuf, const struct run *tmp){ +run_marshal(struct evbuffer *evbuf, const struct run *tmp) { evtag_marshal_string(evbuf, RUN_HOW, tmp->how_data); if (tmp->some_bytes_set) { evtag_marshal(evbuf, RUN_SOME_BYTES, tmp->some_bytes_data, tmp->some_bytes_length); @@ -1084,7 +1087,7 @@ run_marshal(struct evbuffer *evbuf, const struct run *tmp){ } int -run_unmarshal(struct run *tmp, struct evbuffer *evbuf) +run_unmarshal(struct run *tmp, struct evbuffer *evbuf) { ev_uint32_t tag; while (evbuffer_get_length(evbuf) > 0) { @@ -1093,7 +1096,6 @@ run_unmarshal(struct run *tmp, struct evbuffer *evbuf) switch (tag) { case RUN_HOW: - if (tmp->how_set) return (-1); if (evtag_unmarshal_string(evbuf, RUN_HOW, &tmp->how_data) == -1) { @@ -1102,9 +1104,7 @@ run_unmarshal(struct run *tmp, struct evbuffer *evbuf) } tmp->how_set = 1; break; - case RUN_SOME_BYTES: - if (tmp->some_bytes_set) return (-1); if (evtag_payload_length(evbuf, &tmp->some_bytes_length) == -1) @@ -1119,9 +1119,7 @@ run_unmarshal(struct run *tmp, struct evbuffer *evbuf) } tmp->some_bytes_set = 1; break; - case RUN_FIXED_BYTES: - if (tmp->fixed_bytes_set) return (-1); if (evtag_unmarshal_fixed(evbuf, RUN_FIXED_BYTES, tmp->fixed_bytes_data, (24)) == -1) { @@ -1130,9 +1128,7 @@ run_unmarshal(struct run *tmp, struct evbuffer *evbuf) } tmp->fixed_bytes_set = 1; break; - case RUN_NOTES: - if (tmp->notes_length >= tmp->notes_num_allocated && run_notes_expand_to_hold_more(tmp) < 0) { puts("HEY NOW"); @@ -1145,9 +1141,7 @@ run_unmarshal(struct run *tmp, struct evbuffer *evbuf) ++tmp->notes_length; tmp->notes_set = 1; break; - case RUN_LARGE_NUMBER: - if (tmp->large_number_set) return (-1); if (evtag_unmarshal_int64(evbuf, RUN_LARGE_NUMBER, &tmp->large_number_data) == -1) { @@ -1156,9 +1150,7 @@ run_unmarshal(struct run *tmp, struct evbuffer *evbuf) } tmp->large_number_set = 1; break; - case RUN_OTHER_NUMBERS: - if (tmp->other_numbers_length >= tmp->other_numbers_num_allocated && run_other_numbers_expand_to_hold_more(tmp) < 0) { puts("HEY NOW"); @@ -1171,7 +1163,6 @@ run_unmarshal(struct run *tmp, struct evbuffer *evbuf) ++tmp->other_numbers_length; tmp->other_numbers_set = 1; break; - default: return -1; } @@ -1193,7 +1184,8 @@ run_complete(struct run *msg) } int -evtag_unmarshal_run(struct evbuffer *evbuf, ev_uint32_t need_tag, struct run *msg) +evtag_unmarshal_run(struct evbuffer *evbuf, ev_uint32_t need_tag, + struct run *msg) { ev_uint32_t tag; int res = -1; @@ -1214,12 +1206,13 @@ evtag_unmarshal_run(struct evbuffer *evbuf, ev_uint32_t need_tag, struct run *ms } void -evtag_marshal_run(struct evbuffer *evbuf, ev_uint32_t tag, const struct run *msg) +evtag_marshal_run(struct evbuffer *evbuf, ev_uint32_t tag, + const struct run *msg) { struct evbuffer *buf_ = evbuffer_new(); assert(buf_ != NULL); run_marshal(buf_, msg); evtag_marshal_buffer(evbuf, tag, buf_); - evbuffer_free(buf_); + evbuffer_free(buf_); } diff --git a/test/regress.gen.h b/test/regress.gen.h index 86b8d7f59bbc..b3683bd29428 100644 --- a/test/regress.gen.h +++ b/test/regress.gen.h @@ -1,13 +1,14 @@ + /* - * Automatically generated from ./test/regress.rpc + * Automatically generated from ../test/regress.rpc */ -#ifndef EVENT_RPCOUT___TEST_REGRESS_RPC_ -#define EVENT_RPCOUT___TEST_REGRESS_RPC_ +#ifndef EVENT_RPCOUT____TEST_REGRESS_RPC_ +#define EVENT_RPCOUT____TEST_REGRESS_RPC_ + #include <event2/util.h> /* for ev_uint*_t */ #include <event2/rpc.h> - struct msg; struct kill; struct run; @@ -204,4 +205,4 @@ int run_other_numbers_get(struct run *, int, ev_uint32_t *); ev_uint32_t * run_other_numbers_add(struct run *msg, const ev_uint32_t value); /* --- run done --- */ -#endif /* EVENT_RPCOUT___TEST_REGRESS_RPC_ */ +#endif /* EVENT_RPCOUT____TEST_REGRESS_RPC_ */
\ No newline at end of file diff --git a/test/regress.h b/test/regress.h index de1aed30895b..43cb4eaf1e69 100644 --- a/test/regress.h +++ b/test/regress.h @@ -43,6 +43,7 @@ extern struct testcase_t bufferevent_iocp_testcases[]; extern struct testcase_t util_testcases[]; extern struct testcase_t signal_testcases[]; extern struct testcase_t http_testcases[]; +extern struct testcase_t http_iocp_testcases[]; extern struct testcase_t dns_testcases[]; extern struct testcase_t rpc_testcases[]; extern struct testcase_t edgetriggered_testcases[]; @@ -94,6 +95,7 @@ extern int libevent_tests_running_in_debug_mode; #define TT_NO_LOGS (TT_FIRST_USER_FLAG<<5) #define TT_ENABLE_IOCP_FLAG (TT_FIRST_USER_FLAG<<6) #define TT_ENABLE_IOCP (TT_ENABLE_IOCP_FLAG|TT_NEED_THREADS) +#define TT_ENABLE_DEBUG_MODE (TT_ENABLE_IOCP_FLAG<<7) /* All the flags that a legacy test needs. */ #define TT_ISOLATED TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_BASE @@ -132,11 +134,14 @@ pid_t regress_fork(void); #ifdef EVENT__HAVE_OPENSSL #include <openssl/ssl.h> EVP_PKEY *ssl_getkey(void); -X509 *ssl_getcert(void); +X509 *ssl_getcert(EVP_PKEY *key); SSL_CTX *get_ssl_ctx(void); void init_ssl(void); #endif +void * basic_test_setup(const struct testcase_t *testcase); +int basic_test_cleanup(const struct testcase_t *testcase, void *ptr); + #ifdef __cplusplus } #endif diff --git a/test/regress_buffer.c b/test/regress_buffer.c index 1af75f537118..f259b924bff0 100644 --- a/test/regress_buffer.c +++ b/test/regress_buffer.c @@ -63,6 +63,8 @@ #include "regress.h" +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + /* Validates that an evbuffer is good. Returns false if it isn't, true if it * is*/ static int @@ -294,33 +296,39 @@ no_cleanup(const void *data, size_t datalen, void *extra) static void test_evbuffer_remove_buffer_with_empty(void *ptr) { - struct evbuffer *src = evbuffer_new(); - struct evbuffer *dst = evbuffer_new(); - char buf[2]; + struct evbuffer *src = evbuffer_new(); + struct evbuffer *dst = evbuffer_new(); + char buf[2] = { 'A', 'A' }; + + evbuffer_validate(src); + evbuffer_validate(dst); - evbuffer_validate(src); - evbuffer_validate(dst); + /* setup the buffers */ + /* we need more data in src than we will move later */ + evbuffer_add_reference(src, buf, sizeof(buf), no_cleanup, NULL); + evbuffer_add_reference(src, buf, sizeof(buf), no_cleanup, NULL); + /* we need one buffer in dst and one empty buffer at the end */ + evbuffer_add(dst, buf, sizeof(buf)); + evbuffer_add_reference(dst, buf, 0, no_cleanup, NULL); - /* setup the buffers */ - /* we need more data in src than we will move later */ - evbuffer_add_reference(src, buf, sizeof(buf), no_cleanup, NULL); - evbuffer_add_reference(src, buf, sizeof(buf), no_cleanup, NULL); - /* we need one buffer in dst and one empty buffer at the end */ - evbuffer_add(dst, buf, sizeof(buf)); - evbuffer_add_reference(dst, buf, 0, no_cleanup, NULL); + evbuffer_validate(src); + evbuffer_validate(dst); + + tt_mem_op(evbuffer_pullup(src, -1), ==, "AAAA", 4); + tt_mem_op(evbuffer_pullup(dst, -1), ==, "AA", 2); - evbuffer_validate(src); - evbuffer_validate(dst); + /* move three bytes over */ + evbuffer_remove_buffer(src, dst, 3); - /* move three bytes over */ - evbuffer_remove_buffer(src, dst, 3); + evbuffer_validate(src); + evbuffer_validate(dst); - evbuffer_validate(src); - evbuffer_validate(dst); + tt_mem_op(evbuffer_pullup(src, -1), ==, "A", 1); + tt_mem_op(evbuffer_pullup(dst, -1), ==, "AAAAA", 5); -end: - evbuffer_free(src); - evbuffer_free(dst); + end: + evbuffer_free(src); + evbuffer_free(dst); } static void @@ -350,6 +358,9 @@ test_evbuffer_remove_buffer_with_empty2(void *ptr) evbuffer_validate(src); evbuffer_validate(dst); + tt_mem_op(evbuffer_pullup(src, -1), ==, "foofoofoo", 9); + tt_mem_op(evbuffer_pullup(dst, -1), ==, "foofoofoo", 9); + evbuffer_remove_buffer(src, dst, 8); evbuffer_validate(src); @@ -358,6 +369,9 @@ test_evbuffer_remove_buffer_with_empty2(void *ptr) tt_int_op(evbuffer_get_length(src), ==, 1); tt_int_op(evbuffer_get_length(dst), ==, 17); + tt_mem_op(evbuffer_pullup(src, -1), ==, "o", 1); + tt_mem_op(evbuffer_pullup(dst, -1), ==, "foofoofoofoofoofo", 17); + end: evbuffer_free(src); evbuffer_free(dst); @@ -391,6 +405,9 @@ test_evbuffer_remove_buffer_with_empty3(void *ptr) evbuffer_validate(src); evbuffer_validate(dst); + tt_mem_op(evbuffer_pullup(src, -1), ==, "foofoo", 6); + tt_mem_op(evbuffer_pullup(dst, -1), ==, "foofoo", 6); + evbuffer_remove_buffer(src, dst, 5); evbuffer_validate(src); @@ -399,6 +416,9 @@ test_evbuffer_remove_buffer_with_empty3(void *ptr) tt_int_op(evbuffer_get_length(src), ==, 1); tt_int_op(evbuffer_get_length(dst), ==, 11); + tt_mem_op(evbuffer_pullup(src, -1), ==, "o", 1); + tt_mem_op(evbuffer_pullup(dst, -1), ==, "foofoofoofo", 11); + end: evbuffer_free(src); evbuffer_free(dst); @@ -406,6 +426,119 @@ test_evbuffer_remove_buffer_with_empty3(void *ptr) } static void +test_evbuffer_pullup_with_empty(void *ptr) +{ + struct evbuffer *buf = NULL; + + buf = evbuffer_new(); + evbuffer_add(buf, "foo", 3); + evbuffer_add_reference(buf, NULL, 0, NULL, NULL); + evbuffer_validate(buf); + tt_int_op(evbuffer_get_length(buf), ==, 3); + tt_mem_op(evbuffer_pullup(buf, -1), ==, "foo", 3); + + evbuffer_free(buf); + buf = evbuffer_new(); + evbuffer_validate(buf); + tt_int_op(evbuffer_get_length(buf), ==, 0); + tt_int_op(evbuffer_pullup(buf, -1), ==, NULL); + + evbuffer_free(buf); + buf = evbuffer_new(); + evbuffer_add(buf, "foo", 3); + evbuffer_add_reference(buf, NULL, 0, NULL, NULL); + evbuffer_validate(buf); + tt_mem_op(evbuffer_pullup(buf, 3), ==, "foo", 3); + + end: + if (buf) + evbuffer_free(buf); +} + +static void +test_evbuffer_remove_buffer_with_empty_front(void *ptr) +{ + struct evbuffer *buf1 = NULL, *buf2 = NULL; + + buf1 = evbuffer_new(); + tt_assert(buf1); + + buf2 = evbuffer_new(); + tt_assert(buf2); + + tt_int_op(evbuffer_add_reference(buf1, "foo", 3, NULL, NULL), ==, 0); + tt_int_op(evbuffer_prepend(buf1, "", 0), ==, 0); + tt_int_op(evbuffer_remove_buffer(buf1, buf2, 1), ==, 1); + tt_int_op(evbuffer_add(buf1, "bar", 3), ==, 0); + tt_mem_op(evbuffer_pullup(buf1, -1), ==, "oobar", 5); + + evbuffer_validate(buf1); + evbuffer_validate(buf2); + + end: + if (buf1) + evbuffer_free(buf1); + if (buf2) + evbuffer_free(buf2); +} + +static void +test_evbuffer_remove_buffer_adjust_last_with_datap_with_empty(void *ptr) +{ + struct evbuffer *buf1 = NULL, *buf2 = NULL; + + buf1 = evbuffer_new(); + tt_assert(buf1); + + buf2 = evbuffer_new(); + tt_assert(buf2); + + tt_int_op(evbuffer_add(buf1, "aaaaaa", 6), ==, 0); + + // buf1: aaaaaab + // buf2: + { + struct evbuffer_iovec iovecs[2]; + /** we want two chains, to leave one chain empty */ + tt_int_op(evbuffer_reserve_space(buf1, 971, iovecs, 2), ==, 2); + tt_int_op(iovecs[0].iov_len, >=, 1); + tt_int_op(iovecs[1].iov_len, >=, 1); + tt_assert(*(char *)(iovecs[0].iov_base) = 'b'); + tt_assert(iovecs[0].iov_len = 1); + tt_int_op(evbuffer_commit_space(buf1, iovecs, 1), ==, 0); + } + + // buf1: aaaaaab + // buf2: dddcc + tt_int_op(evbuffer_add(buf2, "cc", 2), ==, 0); + tt_int_op(evbuffer_prepend(buf2, "ddd", 3), ==, 0); + + // buf1: + // buf2: aaaaaabdddcc + tt_int_op(evbuffer_prepend_buffer(buf2, buf1), ==, 0); + + // buf1: aaaaaabdddcc + // buf2: + tt_int_op(evbuffer_add_buffer(buf1, buf2), ==, 0); + + // buf1: c + // buf2: aaaaaabdddc + tt_int_op(evbuffer_remove_buffer(buf1, buf2, 11), ==, 11); + + // This fails today, we observe "aaaaaabcddd" instead! + tt_mem_op(evbuffer_pullup(buf2, -1), ==, "aaaaaabdddc", 11); + + evbuffer_validate(buf1); + evbuffer_validate(buf2); + + end: + if (buf1) + evbuffer_free(buf1); + if (buf2) + evbuffer_free(buf2); +} + +static void test_evbuffer_add_buffer_with_empty(void *ptr) { struct evbuffer *src = evbuffer_new(); @@ -635,6 +768,63 @@ end: } static void +test_evbuffer_reserve_with_empty(void *ptr) +{ + struct evbuffer *buf; + struct evbuffer_iovec v[2]; + + tt_assert(buf = evbuffer_new()); + evbuffer_add(buf, "a", 1); + tt_int_op(evbuffer_reserve_space(buf, 1<<12, v, 2), ==, 2); + v[0].iov_len = 1; + *(char *)v[0].iov_base = 'b'; + tt_int_op(evbuffer_commit_space(buf, v, 1), ==, 0); + evbuffer_add(buf, "c", 1); + tt_mem_op(evbuffer_pullup(buf, -1), ==, "abc", 2); + + evbuffer_validate(buf); + + end: + if (buf) + evbuffer_free(buf); +} + +/* regression for evbuffer_expand_fast_() with invalid last_with_datap that has + * been left after evbuffer_prepend() with empty chain in it */ +static void +test_evbuffer_reserve_invalid_last_with_datap(void *ptr) +{ + struct evbuffer *buf = NULL; + struct evbuffer_iovec vec[2]; + const int nvec = ARRAY_SIZE(vec); + int i, avec; + + buf = evbuffer_new(); + tt_assert(buf); + + /* prepend with an empty chain */ + evbuffer_add_reference(buf, "", 0, NULL, NULL); + evbuffer_prepend(buf, "foo", 3); + /* after invalid last_with_datap will create new chain */ + evbuffer_add(buf, "", 0); + /* we need to create at least 2 "used" (in evbuffer_expand_fast_()) chains */ + tt_int_op(avec = evbuffer_reserve_space(buf, 1<<12, vec, nvec), >=, 1); + for (i = 0; i < avec; ++i) + vec[i].iov_len = 0; + tt_int_op(evbuffer_commit_space(buf, vec, avec), ==, 0); + + /* and an actual problem, that triggers an assert(chain == buf->first) in + * evbuffer_expand_fast_() */ + tt_int_op(evbuffer_reserve_space(buf, 1<<13, vec, nvec), >=, 1); + + evbuffer_validate(buf); + +end: + if (buf) + evbuffer_free(buf); +} + +static void test_evbuffer_expand(void *ptr) { char data[4096]; @@ -1796,12 +1986,12 @@ test_evbuffer_callbacks(void *ptr) tt_assert(cb1 != NULL); cb2 = evbuffer_add_cb(buf, log_change_callback, buf_out2); tt_assert(cb2 != NULL); - evbuffer_setcb(buf, self_draining_callback, NULL); + tt_int_op(evbuffer_setcb(buf, self_draining_callback, NULL), ==, 0); evbuffer_add_printf(buf, "This should get drained right away."); tt_uint_op(evbuffer_get_length(buf), ==, 0); tt_uint_op(evbuffer_get_length(buf_out1), ==, 0); tt_uint_op(evbuffer_get_length(buf_out2), ==, 0); - evbuffer_setcb(buf, NULL, NULL); + tt_int_op(evbuffer_setcb(buf, NULL, NULL), ==, 0); evbuffer_add_printf(buf, "This will not."); tt_str_op((const char *) evbuffer_pullup(buf, -1), ==, "This will not."); evbuffer_validate(buf); @@ -1827,6 +2017,14 @@ test_evbuffer_callbacks(void *ptr) "0->15; 15->11; 11->0; "); #endif + /* the next call to readline should fail */ +#ifndef EVENT__DISABLE_MM_REPLACEMENT + event_set_mem_functions(failing_malloc, realloc, free); + tt_int_op(evbuffer_setcb(buf, self_draining_callback, NULL), ==, -1); + evbuffer_validate(buf); + event_set_mem_functions(malloc, realloc, free); +#endif + end: if (buf) evbuffer_free(buf); @@ -2118,6 +2316,58 @@ end: } static void +test_evbuffer_empty_reference_prepend(void *ptr) +{ + struct evbuffer *buf = NULL; + + buf = evbuffer_new(); + tt_assert(buf); + + /** empty chain could leave invalid last_with_datap */ + evbuffer_add_reference(buf, "", 0, NULL, NULL); + evbuffer_validate(buf); + evbuffer_prepend(buf, "foo", 3); + + evbuffer_validate(buf); + tt_assert(!strncmp((char *)evbuffer_pullup(buf, -1), "foo", 3)); + evbuffer_validate(buf); + +end: + if (buf) + evbuffer_free(buf); +} +static void +test_evbuffer_empty_reference_prepend_buffer(void *ptr) +{ + struct evbuffer *buf1 = NULL, *buf2 = NULL; + + buf1 = evbuffer_new(); + tt_assert(buf1); + buf2 = evbuffer_new(); + tt_assert(buf2); + + /** empty chain could leave invalid last_with_datap */ + evbuffer_add_reference(buf1, "", 0, NULL, NULL); + evbuffer_validate(buf1); + evbuffer_add(buf2, "foo", 3); + evbuffer_validate(buf2); + evbuffer_prepend_buffer(buf2, buf1); + evbuffer_validate(buf2); + + tt_assert(!strncmp((char *)evbuffer_pullup(buf2, -1), "foo", 3)); + evbuffer_validate(buf2); + + tt_assert(evbuffer_pullup(buf1, -1) == NULL); + evbuffer_validate(buf2); + +end: + if (buf1) + evbuffer_free(buf1); + if (buf2) + evbuffer_free(buf2); +} + +static void test_evbuffer_peek_first_gt(void *info) { struct evbuffer *buf = NULL, *tmp_buf = NULL; @@ -2274,28 +2524,37 @@ end: static void test_evbuffer_freeze(void *ptr) { - struct evbuffer *buf = NULL, *tmp_buf=NULL; + struct basic_test_data *testdata = ptr; + evutil_socket_t *pair = testdata->pair; + struct evbuffer *buf = NULL, *buf_two = NULL, *tmp_buf = NULL; const char string[] = /* Year's End, Richard Wilbur */ "I've known the wind by water banks to shake\n" "The late leaves down, which frozen where they fell\n" "And held in ice as dancers in a spell\n" "Fluttered all winter long into a lake..."; - const int start = !strcmp(ptr, "start"); + const int start = !strcmp(testdata->setup_data, "start"); + const char tmpfilecontent[] = "file_freeze_test_file"; char *cp; char charbuf[128]; + char *tmpfilename = NULL; + int fd = -1; int r; - size_t orig_length; + size_t orig_length, len; struct evbuffer_iovec v[1]; if (!start) - tt_str_op(ptr, ==, "end"); + tt_str_op(testdata->setup_data, ==, "end"); buf = evbuffer_new(); + buf_two = evbuffer_new(); tmp_buf = evbuffer_new(); tt_assert(tmp_buf); evbuffer_add(buf, string, strlen(string)); + evbuffer_add(buf_two, "abc", 3); + evbuffer_add(tmp_buf, "xyz", 3); evbuffer_freeze(buf, start); /* Freeze the start or the end.*/ + evbuffer_freeze(buf_two, start); #define FREEZE_EQ(a, startcase, endcase) \ do { \ @@ -2324,7 +2583,22 @@ test_evbuffer_freeze(void *ptr) FREEZE_EQ(r, 0, -1); r = evbuffer_add_printf(buf, "Hello %s", "world"); FREEZE_EQ(r, 11, -1); - /* TODO: test add_buffer, add_file, read */ + + r = evbuffer_add_buffer(buf, tmp_buf); + FREEZE_EQ(r, 0, -1); + len = strlen(tmpfilecontent); + fd = regress_make_tmpfile(tmpfilecontent, len, &tmpfilename); + r = evbuffer_add_file(buf, fd, 0, len); + FREEZE_EQ(r, 0, -1); + + if (start) + evbuffer_add(tmp_buf, "xyz", 3); + + tt_assert(evbuffer_get_length(tmp_buf)); + len = evbuffer_get_length(tmp_buf); + evbuffer_write(tmp_buf, pair[0]); + r = evbuffer_read(buf, pair[1], -1); + FREEZE_EQ(r, len, -1); if (!start) tt_int_op(orig_length, ==, evbuffer_get_length(buf)); @@ -2342,7 +2616,24 @@ test_evbuffer_freeze(void *ptr) FREEZE_EQ(cp==NULL, 1, 0); if (cp) free(cp); - /* TODO: Test remove_buffer, add_buffer, write, prepend_buffer */ + + evbuffer_add(tmp_buf, "xyz", 3); + tt_assert(evbuffer_get_length(tmp_buf)); + r = evbuffer_remove_buffer(buf, tmp_buf, 3); + FREEZE_EQ(r, -1, 3); + r = evbuffer_drain(buf, 3); + FREEZE_EQ(r, -1, 0); + r = evbuffer_prepend_buffer(buf, tmp_buf); + FREEZE_EQ(r, -1, 0); + + len = evbuffer_get_length(buf); + r = evbuffer_write(buf, pair[0]); + evbuffer_read(tmp_buf, pair[1], -1); + FREEZE_EQ(r, -1, len); + len = evbuffer_get_length(buf_two); + r = evbuffer_write_atmost(buf_two, pair[0], -1); + evbuffer_read(tmp_buf, pair[1], -1); + FREEZE_EQ(r, -1, len); if (start) tt_int_op(orig_length, ==, evbuffer_get_length(buf)); @@ -2351,8 +2642,16 @@ end: if (buf) evbuffer_free(buf); + if (buf_two) + evbuffer_free(buf_two); + if (tmp_buf) evbuffer_free(tmp_buf); + + if (tmpfilename) { + unlink(tmpfilename); + free(tmpfilename); + } } static void @@ -2504,12 +2803,17 @@ struct testcase_t evbuffer_testcases[] = { { "remove_buffer_with_empty", test_evbuffer_remove_buffer_with_empty, 0, NULL, NULL }, { "remove_buffer_with_empty2", test_evbuffer_remove_buffer_with_empty2, 0, NULL, NULL }, { "remove_buffer_with_empty3", test_evbuffer_remove_buffer_with_empty3, 0, NULL, NULL }, + { "remove_buffer_with_empty_front", test_evbuffer_remove_buffer_with_empty_front, 0, NULL, NULL }, + { "remove_buffer_adjust_last_with_datap_with_empty", + test_evbuffer_remove_buffer_adjust_last_with_datap_with_empty, 0, NULL, NULL }, { "add_buffer_with_empty", test_evbuffer_add_buffer_with_empty, 0, NULL, NULL }, { "add_buffer_with_empty2", test_evbuffer_add_buffer_with_empty2, 0, NULL, NULL }, { "reserve2", test_evbuffer_reserve2, 0, NULL, NULL }, { "reserve_many", test_evbuffer_reserve_many, 0, NULL, NULL }, { "reserve_many2", test_evbuffer_reserve_many, 0, &nil_setup, (void*)"add" }, { "reserve_many3", test_evbuffer_reserve_many, 0, &nil_setup, (void*)"fill" }, + { "reserve_with_empty", test_evbuffer_reserve_with_empty, 0, NULL, NULL }, + { "reserve_invalid_last_with_datap", test_evbuffer_reserve_invalid_last_with_datap, TT_FORK, NULL, NULL }, { "expand", test_evbuffer_expand, 0, NULL, NULL }, { "expand_overflow", test_evbuffer_expand_overflow, 0, NULL, NULL }, { "add1", test_evbuffer_add1, 0, NULL, NULL }, @@ -2527,13 +2831,16 @@ struct testcase_t evbuffer_testcases[] = { { "multicast", test_evbuffer_multicast, 0, NULL, NULL }, { "multicast_drain", test_evbuffer_multicast_drain, 0, NULL, NULL }, { "prepend", test_evbuffer_prepend, TT_FORK, NULL, NULL }, + { "empty_reference_prepend", test_evbuffer_empty_reference_prepend, TT_FORK, NULL, NULL }, + { "empty_reference_prepend_buffer", test_evbuffer_empty_reference_prepend_buffer, TT_FORK, NULL, NULL }, { "peek", test_evbuffer_peek, 0, NULL, NULL }, { "peek_first_gt", test_evbuffer_peek_first_gt, 0, NULL, NULL }, - { "freeze_start", test_evbuffer_freeze, 0, &nil_setup, (void*)"start" }, - { "freeze_end", test_evbuffer_freeze, 0, &nil_setup, (void*)"end" }, + { "freeze_start", test_evbuffer_freeze, TT_NEED_SOCKETPAIR, &basic_setup, (void*)"start" }, + { "freeze_end", test_evbuffer_freeze, TT_NEED_SOCKETPAIR, &basic_setup, (void*)"end" }, { "add_iovec", test_evbuffer_add_iovec, 0, NULL, NULL}, { "copyout", test_evbuffer_copyout, 0, NULL, NULL}, { "file_segment_add_cleanup_cb", test_evbuffer_file_segment_add_cleanup_cb, 0, NULL, NULL }, + { "pullup_with_empty", test_evbuffer_pullup_with_empty, 0, NULL, NULL }, #define ADDFILE_TEST(name, parameters) \ { name, test_evbuffer_add_file, TT_FORK|TT_NEED_BASE, \ diff --git a/test/regress_bufferevent.c b/test/regress_bufferevent.c index 249985e99a19..c276a0e5d1c3 100644 --- a/test/regress_bufferevent.c +++ b/test/regress_bufferevent.c @@ -29,6 +29,19 @@ /* The old tests here need assertions to work. */ #undef NDEBUG +/** + * - clang supports __has_feature + * - gcc supports __SANITIZE_ADDRESS__ + * + * Let's set __SANITIZE_ADDRESS__ if __has_feature(address_sanitizer) + */ +#ifndef __has_feature +#define __has_feature(x) 0 +#endif +#if !defined(__SANITIZE_ADDRESS__) && __has_feature(address_sanitizer) +#define __SANITIZE_ADDRESS__ +#endif + #ifdef _WIN32 #include <winsock2.h> #include <windows.h> @@ -137,14 +150,14 @@ test_bufferevent_impl(int use_pair, int flush) bev2 = pair[1]; bufferevent_setcb(bev1, readcb, writecb, errorcb, bev1); bufferevent_setcb(bev2, readcb, writecb, errorcb, NULL); - tt_int_op(bufferevent_getfd(bev1), ==, -1); + tt_fd_op(bufferevent_getfd(bev1), ==, EVUTIL_INVALID_SOCKET); tt_ptr_op(bufferevent_get_underlying(bev1), ==, NULL); tt_ptr_op(bufferevent_pair_get_partner(bev1), ==, bev2); tt_ptr_op(bufferevent_pair_get_partner(bev2), ==, bev1); } else { bev1 = bufferevent_new(pair[0], readcb, writecb, errorcb, NULL); bev2 = bufferevent_new(pair[1], readcb, writecb, errorcb, NULL); - tt_int_op(bufferevent_getfd(bev1), ==, pair[0]); + tt_fd_op(bufferevent_getfd(bev1), ==, pair[0]); tt_ptr_op(bufferevent_get_underlying(bev1), ==, NULL); tt_ptr_op(bufferevent_pair_get_partner(bev1), ==, NULL); tt_ptr_op(bufferevent_pair_get_partner(bev2), ==, NULL); @@ -203,7 +216,7 @@ static void test_bufferevent_pair_flush_normal(void) { test_bufferevent_impl(1, static void test_bufferevent_pair_flush_flush(void) { test_bufferevent_impl(1, BEV_FLUSH); } static void test_bufferevent_pair_flush_finished(void) { test_bufferevent_impl(1, BEV_FINISHED); } -#if defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED) +#if defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED) && !defined(__SANITIZE_ADDRESS__) /** * Trace lock/unlock/alloc/free for locks. * (More heavier then evthread_debug*) @@ -569,8 +582,8 @@ test_bufferevent_filters_impl(int use_pair, int disable) tt_ptr_op(bufferevent_get_underlying(bev1), ==, bev1_base); tt_ptr_op(bufferevent_get_underlying(bev2), ==, bev2_base); - tt_int_op(bufferevent_getfd(bev1), ==, -1); - tt_int_op(bufferevent_getfd(bev2), ==, -1); + tt_fd_op(bufferevent_getfd(bev1), ==, bufferevent_getfd(bev1_base)); + tt_fd_op(bufferevent_getfd(bev2), ==, bufferevent_getfd(bev2_base)); bufferevent_disable(bev1, EV_READ); bufferevent_enable(bev2, EV_READ); @@ -640,7 +653,7 @@ end: ; } -static int +static evutil_socket_t fake_listener_create(struct sockaddr_in *localhost) { struct sockaddr *sa = (struct sockaddr *)localhost; @@ -788,15 +801,28 @@ end: } static void +close_socket_cb(evutil_socket_t fd, short what, void *arg) +{ + evutil_socket_t *fdp = arg; + if (*fdp >= 0) { + evutil_closesocket(*fdp); + *fdp = -1; + } +} + +static void test_bufferevent_connect_fail_eventcb(void *arg) { struct basic_test_data *data = arg; int flags = BEV_OPT_CLOSE_ON_FREE | (long)data->setup_data; + struct event close_listener_event; struct bufferevent *bev = NULL; struct evconnlistener *lev = NULL; struct sockaddr_in localhost; + struct timeval close_timeout = { 0, 300000 }; ev_socklen_t slen = sizeof(localhost); evutil_socket_t fake_listener = -1; + int r; fake_listener = fake_listener_create(&localhost); @@ -809,10 +835,22 @@ test_bufferevent_connect_fail_eventcb(void *arg) bufferevent_enable(bev, EV_READ|EV_WRITE); tt_int_op(n_events_invoked, ==, 0); tt_int_op(n_reads_invoked, ==, 0); + /** @see also test_bufferevent_connect_fail() */ - bufferevent_socket_connect(bev, (struct sockaddr *)&localhost, slen); + r = bufferevent_socket_connect(bev, (struct sockaddr *)&localhost, slen); + /* XXXX we'd like to test the '0' case everywhere, but FreeBSD tells + * detects the error immediately, which is not really wrong of it. */ + tt_want(r == 0 || r == -1); + tt_int_op(n_events_invoked, ==, 0); tt_int_op(n_reads_invoked, ==, 0); + + /* Close the listener socket after a delay. This should trigger + "connection refused" on some other platforms, including OSX. */ + evtimer_assign(&close_listener_event, data->base, close_socket_cb, + &fake_listener); + event_add(&close_listener_event, &close_timeout); + event_base_dispatch(data->base); tt_int_op(n_events_invoked, ==, 1); tt_int_op(n_reads_invoked, ==, 0); @@ -847,23 +885,13 @@ want_fail_eventcb(struct bufferevent *bev, short what, void *ctx) } static void -close_socket_cb(evutil_socket_t fd, short what, void *arg) -{ - evutil_socket_t *fdp = arg; - if (*fdp >= 0) { - evutil_closesocket(*fdp); - *fdp = -1; - } -} - -static void test_bufferevent_connect_fail(void *arg) { struct basic_test_data *data = (struct basic_test_data *)arg; struct bufferevent *bev=NULL; struct event close_listener_event; int close_listener_event_added = 0; - struct timeval one_second = { 1, 0 }; + struct timeval close_timeout = { 0, 300000 }; struct sockaddr_in localhost; ev_socklen_t slen = sizeof(localhost); evutil_socket_t fake_listener = -1; @@ -882,11 +910,11 @@ test_bufferevent_connect_fail(void *arg) * detects the error immediately, which is not really wrong of it. */ tt_want(r == 0 || r == -1); - /* Close the listener socket after a second. This should trigger + /* Close the listener socket after a delay. This should trigger "connection refused" on some other platforms, including OSX. */ evtimer_assign(&close_listener_event, data->base, close_socket_cb, &fake_listener); - event_add(&close_listener_event, &one_second); + event_add(&close_listener_event, &close_timeout); close_listener_event_added = 1; event_base_dispatch(data->base); @@ -908,18 +936,24 @@ struct timeout_cb_result { struct timeval read_timeout_at; struct timeval write_timeout_at; struct timeval last_wrote_at; + struct timeval last_read_at; int n_read_timeouts; int n_write_timeouts; int total_calls; }; static void +bev_timeout_read_cb(struct bufferevent *bev, void *arg) +{ + struct timeout_cb_result *res = arg; + evutil_gettimeofday(&res->last_read_at, NULL); +} +static void bev_timeout_write_cb(struct bufferevent *bev, void *arg) { struct timeout_cb_result *res = arg; evutil_gettimeofday(&res->last_wrote_at, NULL); } - static void bev_timeout_event_cb(struct bufferevent *bev, short what, void *arg) { @@ -947,7 +981,6 @@ test_bufferevent_timeouts(void *arg) int use_pair = 0, use_filter = 0; struct timeval tv_w, tv_r, started_at; struct timeout_cb_result res1, res2; - char buf[1024]; memset(&res1, 0, sizeof(res1)); memset(&res2, 0, sizeof(res2)); @@ -966,7 +999,6 @@ test_bufferevent_timeouts(void *arg) bev1 = bufferevent_socket_new(data->base, data->pair[0], 0); bev2 = bufferevent_socket_new(data->base, data->pair[1], 0); } - tt_assert(bev1); tt_assert(bev2); @@ -990,30 +1022,14 @@ test_bufferevent_timeouts(void *arg) tv_w.tv_sec = tv_r.tv_sec = 0; tv_w.tv_usec = 100*1000; tv_r.tv_usec = 150*1000; - bufferevent_setcb(bev1, NULL, bev_timeout_write_cb, + bufferevent_setcb(bev1, bev_timeout_read_cb, bev_timeout_write_cb, bev_timeout_event_cb, &res1); - bufferevent_setwatermark(bev1, EV_WRITE, 1024*1024+10, 0); bufferevent_set_timeouts(bev1, &tv_r, &tv_w); - if (use_pair) { - /* For a pair, the fact that the other side isn't reading - * makes the writer stall */ - bufferevent_write(bev1, "ABCDEFG", 7); - } else { - /* For a real socket, the kernel's TCP buffers can eat a - * fair number of bytes; make sure that at some point we - * have some bytes that will stall. */ - struct evbuffer *output = bufferevent_get_output(bev1); - int i; - memset(buf, 0xbb, sizeof(buf)); - for (i=0;i<1024;++i) { - evbuffer_add_reference(output, buf, sizeof(buf), - NULL, NULL); - } - } + bufferevent_write(bev1, "ABCDEFG", 7); bufferevent_enable(bev1, EV_READ|EV_WRITE); /* bev2 has nothing to say, and isn't listening. */ - bufferevent_setcb(bev2, NULL, bev_timeout_write_cb, + bufferevent_setcb(bev2, bev_timeout_read_cb, bev_timeout_write_cb, bev_timeout_event_cb, &res2); tv_w.tv_sec = tv_r.tv_sec = 0; tv_w.tv_usec = 200*1000; @@ -1030,15 +1046,26 @@ test_bufferevent_timeouts(void *arg) /* XXXX Test that actually reading or writing a little resets the * timeouts. */ - /* Each buf1 timeout happens, and happens only once. */ - tt_want(res1.n_read_timeouts); - tt_want(res1.n_write_timeouts); + tt_want(res1.total_calls == 2); tt_want(res1.n_read_timeouts == 1); tt_want(res1.n_write_timeouts == 1); + tt_want(res2.total_calls == !(use_pair && !use_filter)); + tt_want(res2.n_write_timeouts == !(use_pair && !use_filter)); + tt_want(!res2.n_read_timeouts); test_timeval_diff_eq(&started_at, &res1.read_timeout_at, 150); test_timeval_diff_eq(&started_at, &res1.write_timeout_at, 100); +#define tt_assert_timeval_empty(tv) do { \ + tt_int_op((tv).tv_sec, ==, 0); \ + tt_int_op((tv).tv_usec, ==, 0); \ +} while(0) + tt_assert_timeval_empty(res1.last_read_at); + tt_assert_timeval_empty(res2.last_read_at); + tt_assert_timeval_empty(res2.last_wrote_at); + tt_assert_timeval_empty(res2.last_wrote_at); +#undef tt_assert_timeval_empty + end: if (bev1) bufferevent_free(bev1); @@ -1337,9 +1364,9 @@ struct testcase_t bufferevent_testcases[] = { LEGACY(bufferevent_pair_flush_normal, TT_ISOLATED), LEGACY(bufferevent_pair_flush_flush, TT_ISOLATED), LEGACY(bufferevent_pair_flush_finished, TT_ISOLATED), -#if defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED) +#if defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED) && !defined(__SANITIZE_ADDRESS__) { "bufferevent_pair_release_lock", test_bufferevent_pair_release_lock, - TT_FORK|TT_ISOLATED|TT_NEED_THREADS|TT_NEED_BASE|TT_LEGACY, + TT_FORK|TT_ISOLATED|TT_NEED_THREADS|TT_NEED_BASE|TT_LEGACY|TT_NO_LOGS, &basic_setup, NULL }, #endif LEGACY(bufferevent_watermarks, TT_ISOLATED), @@ -1363,7 +1390,7 @@ struct testcase_t bufferevent_testcases[] = { { "bufferevent_connect_fail", test_bufferevent_connect_fail, TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, { "bufferevent_timeout", test_bufferevent_timeouts, - TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR, &basic_setup, (void*)"" }, + TT_FORK|TT_NEED_BASE, &basic_setup, (void*)"" }, { "bufferevent_timeout_pair", test_bufferevent_timeouts, TT_FORK|TT_NEED_BASE, &basic_setup, (void*)"pair" }, { "bufferevent_timeout_filter", test_bufferevent_timeouts, @@ -1406,38 +1433,35 @@ struct testcase_t bufferevent_testcases[] = { END_OF_TESTCASES, }; +#define TT_IOCP (TT_FORK|TT_NEED_BASE|TT_ENABLE_IOCP) +#define TT_IOCP_LEGACY (TT_ISOLATED|TT_ENABLE_IOCP) struct testcase_t bufferevent_iocp_testcases[] = { - - LEGACY(bufferevent, TT_ISOLATED|TT_ENABLE_IOCP), + LEGACY(bufferevent, TT_IOCP_LEGACY), LEGACY(bufferevent_flush_normal, TT_ISOLATED), LEGACY(bufferevent_flush_flush, TT_ISOLATED), LEGACY(bufferevent_flush_finished, TT_ISOLATED), - LEGACY(bufferevent_watermarks, TT_ISOLATED|TT_ENABLE_IOCP), - LEGACY(bufferevent_filters, TT_ISOLATED|TT_ENABLE_IOCP), - LEGACY(bufferevent_filters_disable, TT_ISOLATED|TT_ENABLE_IOCP), + LEGACY(bufferevent_watermarks, TT_IOCP_LEGACY), + LEGACY(bufferevent_filters, TT_IOCP_LEGACY), + LEGACY(bufferevent_filters_disable, TT_IOCP_LEGACY), + { "bufferevent_connect", test_bufferevent_connect, - TT_FORK|TT_NEED_BASE|TT_ENABLE_IOCP, &basic_setup, (void*)"" }, + TT_IOCP, &basic_setup, (void*)"" }, { "bufferevent_connect_defer", test_bufferevent_connect, - TT_FORK|TT_NEED_BASE|TT_ENABLE_IOCP, &basic_setup, (void*)"defer" }, + TT_IOCP, &basic_setup, (void*)"defer" }, { "bufferevent_connect_lock", test_bufferevent_connect, - TT_FORK|TT_NEED_BASE|TT_NEED_THREADS|TT_ENABLE_IOCP, &basic_setup, - (void*)"lock" }, + TT_IOCP, &basic_setup, (void*)"lock" }, { "bufferevent_connect_lock_defer", test_bufferevent_connect, - TT_FORK|TT_NEED_BASE|TT_NEED_THREADS|TT_ENABLE_IOCP, &basic_setup, - (void*)"defer lock" }, + TT_IOCP, &basic_setup, (void*)"defer lock" }, { "bufferevent_connect_fail", test_bufferevent_connect_fail, - TT_FORK|TT_NEED_BASE|TT_ENABLE_IOCP, &basic_setup, NULL }, + TT_IOCP, &basic_setup, NULL }, { "bufferevent_connect_nonblocking", test_bufferevent_connect, - TT_FORK|TT_NEED_BASE|TT_ENABLE_IOCP, &basic_setup, - (void*)"unset_connectex" }, + TT_IOCP, &basic_setup, (void*)"unset_connectex" }, { "bufferevent_connect_fail_eventcb_defer", test_bufferevent_connect_fail_eventcb, - TT_FORK|TT_NEED_BASE|TT_ENABLE_IOCP, &basic_setup, - (void*)BEV_OPT_DEFER_CALLBACKS }, + TT_IOCP, &basic_setup, (void*)BEV_OPT_DEFER_CALLBACKS }, { "bufferevent_connect_fail_eventcb", - test_bufferevent_connect_fail_eventcb, - TT_FORK|TT_NEED_BASE|TT_ENABLE_IOCP, &basic_setup, NULL }, + test_bufferevent_connect_fail_eventcb, TT_IOCP, &basic_setup, NULL }, END_OF_TESTCASES, }; diff --git a/test/regress_dns.c b/test/regress_dns.c index 8950440f9731..9a8bff4f1526 100644 --- a/test/regress_dns.c +++ b/test/regress_dns.c @@ -59,6 +59,10 @@ #include <string.h> #include <errno.h> +#ifdef EVENT__HAVE_SYS_RESOURCE_H +#include <sys/resource.h> +#endif + #include "event2/dns.h" #include "event2/dns_compat.h" #include "event2/dns_struct.h" @@ -68,9 +72,12 @@ #include "event2/util.h" #include "event2/listener.h" #include "event2/bufferevent.h" +#include <event2/thread.h> #include "log-internal.h" +#include "evthread-internal.h" #include "regress.h" #include "regress_testutils.h" +#include "regress_thread.h" #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) @@ -197,7 +204,7 @@ dns_resolve_reverse(void *ptr) { struct in_addr in; struct event_base *base = event_base_new(); - struct evdns_base *dns = evdns_base_new(base, 1/* init name servers */); + struct evdns_base *dns = evdns_base_new(base, EVDNS_BASE_INITIALIZE_NAMESERVERS); struct evdns_request *req = NULL; tt_assert(base); @@ -612,16 +619,8 @@ end: if (dns) evdns_base_free(dns, 0); } -static void -dns_search_test(void *arg) -{ - return dns_search_test_impl(arg, 0); -} -static void -dns_search_lower_test(void *arg) -{ - return dns_search_test_impl(arg, 1); -} +static void dns_search_test(void *arg) { dns_search_test_impl(arg, 0); } +static void dns_search_lower_test(void *arg) { dns_search_test_impl(arg, 1); } static int request_count = 0; static struct evdns_request *current_req = NULL; @@ -1005,6 +1004,59 @@ end: event_base_free(inactive_base); } +static void +dns_initialize_nameservers_test(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct evdns_base *dns = NULL; + + dns = evdns_base_new(base, 0); + tt_assert(dns); + tt_int_op(evdns_base_get_nameserver_addr(dns, 0, NULL, 0), ==, -1); + evdns_base_free(dns, 0); + + dns = evdns_base_new(base, EVDNS_BASE_INITIALIZE_NAMESERVERS); + tt_assert(dns); + tt_int_op(evdns_base_get_nameserver_addr(dns, 0, NULL, 0), ==, sizeof(struct sockaddr)); + +end: + if (dns) + evdns_base_free(dns, 0); +} +#ifndef _WIN32 +#define RESOLV_FILE "empty-resolv.conf" +static void +dns_nameservers_no_default_test(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct evdns_base *dns = NULL; + int ok = access(RESOLV_FILE, R_OK); + + tt_assert(ok); + + dns = evdns_base_new(base, 0); + tt_assert(dns); + tt_int_op(evdns_base_get_nameserver_addr(dns, 0, NULL, 0), ==, -1); + + /* We cannot test + * EVDNS_BASE_INITIALIZE_NAMESERVERS|EVDNS_BASE_NAMESERVERS_NO_DEFAULT + * because we cannot mock "/etc/resolv.conf" (yet). */ + + evdns_base_resolv_conf_parse(dns, + DNS_OPTIONS_ALL|DNS_OPTION_NAMESERVERS_NO_DEFAULT, RESOLV_FILE); + tt_int_op(evdns_base_get_nameserver_addr(dns, 0, NULL, 0), ==, -1); + + evdns_base_resolv_conf_parse(dns, DNS_OPTIONS_ALL, RESOLV_FILE); + tt_int_op(evdns_base_get_nameserver_addr(dns, 0, NULL, 0), ==, sizeof(struct sockaddr)); + +end: + if (dns) + evdns_base_free(dns, 0); +} +#endif + /* === Test for bufferevent_socket_connect_hostname */ static int total_connected_or_failed = 0; @@ -1021,7 +1073,7 @@ be_getaddrinfo_server_cb(struct evdns_server_request *req, void *data) int added_any=0; ++*n_got_p; - for (i=0;i<req->nquestions;++i) { + for (i = 0; i < req->nquestions; ++i) { const int qtype = req->questions[i]->type; const int qclass = req->questions[i]->dns_question_class; const char *qname = req->questions[i]->name; @@ -1165,27 +1217,36 @@ static void be_connect_hostname_event_cb(struct bufferevent *bev, short what, void *ctx) { struct be_conn_hostname_result *got = ctx; - if (!got->what) { - TT_BLATHER(("Got a bufferevent event %d", what)); - got->what = what; - - if ((what & BEV_EVENT_CONNECTED) || (what & BEV_EVENT_ERROR)) { - int r; - if ((r = bufferevent_socket_get_dns_error(bev))) { - got->dnserr = r; - TT_BLATHER(("DNS error %d: %s", r, - evutil_gai_strerror(r))); - } ++total_connected_or_failed; - TT_BLATHER(("Got %d connections or errors.", total_connected_or_failed)); - - if (total_n_accepted >= 3 && total_connected_or_failed >= 5) - event_base_loopexit(be_connect_hostname_base, - NULL); - } - } else { + + if (got->what) { TT_FAIL(("Two events on one bufferevent. %d,%d", got->what, (int)what)); } + + TT_BLATHER(("Got a bufferevent event %d", what)); + got->what = what; + + if ((what & BEV_EVENT_CONNECTED) || (what & BEV_EVENT_ERROR)) { + int expected = 3; + int r = bufferevent_socket_get_dns_error(bev); + + if (r) { + got->dnserr = r; + TT_BLATHER(("DNS error %d: %s", r, + evutil_gai_strerror(r))); + } + ++total_connected_or_failed; + TT_BLATHER(("Got %d connections or errors.", total_connected_or_failed)); + + /** emfile test */ + if (errno == EMFILE) { + expected = 0; + } + + if (total_n_accepted >= expected && total_connected_or_failed >= 5) + event_base_loopexit(be_connect_hostname_base, + NULL); + } } static void @@ -1193,10 +1254,9 @@ test_bufferevent_connect_hostname(void *arg) { struct basic_test_data *data = arg; struct evconnlistener *listener = NULL; - struct bufferevent *be1=NULL, *be2=NULL, *be3=NULL, *be4=NULL, *be5=NULL; - struct be_conn_hostname_result be1_outcome={0,0}, be2_outcome={0,0}, - be3_outcome={0,0}, be4_outcome={0,0}, be5_outcome={0,0}; - int expect_err5; + struct bufferevent *be[5]; + struct be_conn_hostname_result be_outcome[ARRAY_SIZE(be)]; + int expect_err; struct evdns_base *dns=NULL; struct evdns_server_port *port=NULL; struct sockaddr_in sin; @@ -1204,6 +1264,9 @@ test_bufferevent_connect_hostname(void *arg) ev_uint16_t dns_port=0; int n_accept=0, n_dns=0; char buf[128]; + int emfile = data->setup_data && !strcmp(data->setup_data, "emfile"); + unsigned i; + int ret; be_connect_hostname_base = data->base; @@ -1230,32 +1293,34 @@ test_bufferevent_connect_hostname(void *arg) evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", (int)dns_port); evdns_base_nameserver_ip_add(dns, buf); +#ifdef EVENT__HAVE_SETRLIMIT + if (emfile) { + int fd = socket(AF_INET, SOCK_STREAM, 0); + struct rlimit file = { fd, fd }; + + tt_int_op(fd, >=, 0); + tt_assert(!close(fd)); + + tt_assert(!setrlimit(RLIMIT_NOFILE, &file)); + } +#endif + /* Now, finally, at long last, launch the bufferevents. One should do * a failing lookup IP, one should do a successful lookup by IP, * and one should do a successful lookup by hostname. */ - be1 = bufferevent_socket_new(data->base, -1, BEV_OPT_CLOSE_ON_FREE); - be2 = bufferevent_socket_new(data->base, -1, BEV_OPT_CLOSE_ON_FREE); - be3 = bufferevent_socket_new(data->base, -1, BEV_OPT_CLOSE_ON_FREE); - be4 = bufferevent_socket_new(data->base, -1, BEV_OPT_CLOSE_ON_FREE); - be5 = bufferevent_socket_new(data->base, -1, BEV_OPT_CLOSE_ON_FREE); - - bufferevent_setcb(be1, NULL, NULL, be_connect_hostname_event_cb, - &be1_outcome); - bufferevent_setcb(be2, NULL, NULL, be_connect_hostname_event_cb, - &be2_outcome); - bufferevent_setcb(be3, NULL, NULL, be_connect_hostname_event_cb, - &be3_outcome); - bufferevent_setcb(be4, NULL, NULL, be_connect_hostname_event_cb, - &be4_outcome); - bufferevent_setcb(be5, NULL, NULL, be_connect_hostname_event_cb, - &be5_outcome); + for (i = 0; i < ARRAY_SIZE(be); ++i) { + memset(&be_outcome[i], 0, sizeof(be_outcome[i])); + be[i] = bufferevent_socket_new(data->base, -1, BEV_OPT_CLOSE_ON_FREE); + bufferevent_setcb(be[i], NULL, NULL, be_connect_hostname_event_cb, + &be_outcome[i]); + } /* Use the blocking resolver. This one will fail if your resolver * can't resolve localhost to 127.0.0.1 */ - tt_assert(!bufferevent_socket_connect_hostname(be4, NULL, AF_INET, + tt_assert(!bufferevent_socket_connect_hostname(be[3], NULL, AF_INET, "localhost", listener_port)); /* Use the blocking resolver with a nonexistent hostname. */ - tt_assert(!bufferevent_socket_connect_hostname(be5, NULL, AF_INET, + tt_assert(!bufferevent_socket_connect_hostname(be[4], NULL, AF_INET, "nonesuch.nowhere.example.com", 80)); { /* The blocking resolver will use the system nameserver, which @@ -1266,35 +1331,53 @@ test_bufferevent_connect_hostname(void *arg) hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; - expect_err5 = evutil_getaddrinfo( + expect_err = evutil_getaddrinfo( "nonesuch.nowhere.example.com", "80", &hints, &ai); } /* Launch an async resolve that will fail. */ - tt_assert(!bufferevent_socket_connect_hostname(be1, dns, AF_INET, + tt_assert(!bufferevent_socket_connect_hostname(be[0], dns, AF_INET, "nosuchplace.example.com", listener_port)); /* Connect to the IP without resolving. */ - tt_assert(!bufferevent_socket_connect_hostname(be2, dns, AF_INET, + tt_assert(!bufferevent_socket_connect_hostname(be[1], dns, AF_INET, "127.0.0.1", listener_port)); /* Launch an async resolve that will succeed. */ - tt_assert(!bufferevent_socket_connect_hostname(be3, dns, AF_INET, + tt_assert(!bufferevent_socket_connect_hostname(be[2], dns, AF_INET, "nobodaddy.example.com", listener_port)); - event_base_dispatch(data->base); + ret = event_base_dispatch(data->base); +#ifdef __sun__ + if (emfile && !strcmp(event_base_get_method(data->base), "devpoll")) { + tt_int_op(ret, ==, -1); + /** DP_POLL failed */ + tt_skip(); + } else +#endif + { + tt_int_op(ret, ==, 0); + } - tt_int_op(be1_outcome.what, ==, BEV_EVENT_ERROR); - tt_int_op(be1_outcome.dnserr, ==, EVUTIL_EAI_NONAME); - tt_int_op(be2_outcome.what, ==, BEV_EVENT_CONNECTED); - tt_int_op(be2_outcome.dnserr, ==, 0); - tt_int_op(be3_outcome.what, ==, BEV_EVENT_CONNECTED); - tt_int_op(be3_outcome.dnserr, ==, 0); - tt_int_op(be4_outcome.what, ==, BEV_EVENT_CONNECTED); - tt_int_op(be4_outcome.dnserr, ==, 0); - if (expect_err5) { - tt_int_op(be5_outcome.what, ==, BEV_EVENT_ERROR); - tt_int_op(be5_outcome.dnserr, ==, expect_err5); + tt_int_op(be_outcome[0].what, ==, BEV_EVENT_ERROR); + tt_int_op(be_outcome[0].dnserr, ==, EVUTIL_EAI_NONAME); + tt_int_op(be_outcome[1].what, ==, !emfile ? BEV_EVENT_CONNECTED : BEV_EVENT_ERROR); + tt_int_op(be_outcome[1].dnserr, ==, 0); + tt_int_op(be_outcome[2].what, ==, !emfile ? BEV_EVENT_CONNECTED : BEV_EVENT_ERROR); + tt_int_op(be_outcome[2].dnserr, ==, 0); + tt_int_op(be_outcome[3].what, ==, !emfile ? BEV_EVENT_CONNECTED : BEV_EVENT_ERROR); + if (!emfile) { + tt_int_op(be_outcome[3].dnserr, ==, 0); + } else { + tt_int_op(be_outcome[3].dnserr, !=, 0); + } + if (expect_err) { + tt_int_op(be_outcome[4].what, ==, BEV_EVENT_ERROR); + tt_int_op(be_outcome[4].dnserr, ==, expect_err); } - tt_int_op(n_accept, ==, 3); + if (emfile) { + tt_int_op(n_accept, ==, 0); + } else { + tt_int_op(n_accept, ==, 3); + } tt_int_op(n_dns, ==, 2); end: @@ -1304,16 +1387,10 @@ end: evdns_close_server_port(port); if (dns) evdns_base_free(dns, 0); - if (be1) - bufferevent_free(be1); - if (be2) - bufferevent_free(be2); - if (be3) - bufferevent_free(be3); - if (be4) - bufferevent_free(be4); - if (be5) - bufferevent_free(be5); + for (i = 0; i < ARRAY_SIZE(be); ++i) { + if (be[i]) + bufferevent_free(be[i]); + } } @@ -1352,7 +1429,7 @@ test_getaddrinfo_async(void *arg) struct evutil_addrinfo hints, *a; struct gai_outcome local_outcome; struct gai_outcome a_out[12]; - int i; + unsigned i; struct evdns_getaddrinfo_request *r; char buf[128]; struct evdns_server_port *port = NULL; @@ -1710,7 +1787,7 @@ test_getaddrinfo_async(void *arg) end: if (local_outcome.ai) evutil_freeaddrinfo(local_outcome.ai); - for (i=0;i<(int)ARRAY_SIZE(a_out);++i) { + for (i = 0; i < ARRAY_SIZE(a_out); ++i) { if (a_out[i].ai) evutil_freeaddrinfo(a_out[i].ai); } @@ -1731,7 +1808,8 @@ struct gaic_request_status { #define GAIC_MAGIC 0x1234abcd -static int pending = 0; +static int gaic_pending = 0; +static int gaic_freed = 0; static void gaic_cancel_request_cb(evutil_socket_t fd, short what, void *arg) @@ -1776,7 +1854,13 @@ gaic_getaddrinfo_cb(int result, struct evutil_addrinfo *res, void *arg) free(status); end: - if (--pending <= 0) + if (res) + { + TT_BLATHER(("evutil_freeaddrinfo(%p)", res)); + evutil_freeaddrinfo(res); + ++gaic_freed; + } + if (--gaic_pending <= 0) event_base_loopexit(base, NULL); } @@ -1794,7 +1878,7 @@ gaic_launch(struct event_base *base, struct evdns_base *dns_base) "foobar.bazquux.example.com", "80", NULL, gaic_getaddrinfo_cb, status); event_add(&status->cancel_event, &tv); - ++pending; + ++gaic_pending; } #ifdef EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED @@ -1984,7 +2068,7 @@ test_getaddrinfo_async_cancel_stress(void *ptr) struct sockaddr_in sin; struct sockaddr_storage ss; ev_socklen_t slen; - int i; + unsigned i; base = event_base_new(); dns_base = evdns_base_new(base, 0); @@ -2017,6 +2101,9 @@ test_getaddrinfo_async_cancel_stress(void *ptr) event_base_dispatch(base); + // at least some was canceled via external event + tt_int_op(gaic_freed, !=, 1000); + end: if (dns_base) evdns_base_free(dns_base, 1); @@ -2033,13 +2120,14 @@ dns_client_fail_requests_test(void *arg) { struct basic_test_data *data = arg; struct event_base *base = data->base; + int limit_inflight = data->setup_data && !strcmp(data->setup_data, "limit-inflight"); struct evdns_base *dns = NULL; struct evdns_server_port *dns_port = NULL; ev_uint16_t portnum = 0; char buf[64]; struct generic_dns_callback_result r[20]; - int i; + unsigned i; dns_port = regress_get_dnsserver(base, &portnum, NULL, regress_dns_server_cb, reissue_table); @@ -2050,6 +2138,9 @@ dns_client_fail_requests_test(void *arg) dns = evdns_base_new(base, EVDNS_BASE_DISABLE_WHEN_INACTIVE); tt_assert(!evdns_base_nameserver_ip_add(dns, buf)); + if (limit_inflight) + tt_assert(!evdns_base_set_option(dns, "max-inflight:", "11")); + for (i = 0; i < 20; ++i) evdns_base_resolve_ipv4(dns, "foof.example.com", 0, generic_dns_callback, &r[i]); @@ -2096,7 +2187,7 @@ dns_client_fail_requests_getaddrinfo_test(void *arg) tt_assert(!evdns_base_nameserver_ip_add(dns, buf)); for (i = 0; i < 20; ++i) - tt_assert(evdns_getaddrinfo(dns, "foof.example.com", "ssh", NULL, getaddrinfo_cb, &r[i])); + tt_assert(evdns_getaddrinfo(dns, "foof.example.com", "80", NULL, getaddrinfo_cb, &r[i])); n_replies_left = 20; exit_base = base; @@ -2113,6 +2204,238 @@ end: evdns_close_server_port(dns_port); } +#ifdef EVTHREAD_USE_PTHREADS_IMPLEMENTED +struct race_param +{ + void *lock; + void *reqs_cmpl_cond; + int bw_threads; + void *bw_threads_exited_cond; + volatile int stopping; + void *base; + void *dns; + + int locked; +}; +static void * +race_base_run(void *arg) +{ + struct race_param *rp = (struct race_param *)arg; + event_base_loop(rp->base, EVLOOP_NO_EXIT_ON_EMPTY); + THREAD_RETURN(); +} +static void * +race_busywait_run(void *arg) +{ + struct race_param *rp = (struct race_param *)arg; + struct sockaddr_storage ss; + while (!rp->stopping) + evdns_base_get_nameserver_addr(rp->dns, 0, (struct sockaddr *)&ss, sizeof(ss)); + EVLOCK_LOCK(rp->lock, 0); + if (--rp->bw_threads == 0) + EVTHREAD_COND_SIGNAL(rp->bw_threads_exited_cond); + EVLOCK_UNLOCK(rp->lock, 0); + THREAD_RETURN(); +} +static void +race_gai_cb(int result, struct evutil_addrinfo *res, void *arg) +{ + struct race_param *rp = arg; + (void)result; + (void)res; + + --n_replies_left; + if (n_replies_left == 0) { + EVLOCK_LOCK(rp->lock, 0); + EVTHREAD_COND_SIGNAL(rp->reqs_cmpl_cond); + EVLOCK_UNLOCK(rp->lock, 0); + } +} +static void +getaddrinfo_race_gotresolve_test(void *arg) +{ + struct race_param rp; + struct evdns_server_port *dns_port = NULL; + ev_uint16_t portnum = 0; + char buf[64]; + int i; + + // Some stress is needed to yield inside getaddrinfo between resolve_ipv4 and resolve_ipv6 + int n_reqs = 16384; +#ifdef _SC_NPROCESSORS_ONLN + int n_threads = sysconf(_SC_NPROCESSORS_ONLN) + 1; +#else + int n_threads = 17; +#endif + THREAD_T thread[n_threads]; + struct timeval tv; + + (void)arg; + + evthread_use_pthreads(); + + rp.base = event_base_new(); + tt_assert(rp.base); + if (evthread_make_base_notifiable(rp.base) < 0) + tt_abort_msg("Couldn't make base notifiable!"); + + dns_port = regress_get_dnsserver(rp.base, &portnum, NULL, + regress_dns_server_cb, reissue_table); + tt_assert(dns_port); + + evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", (int)portnum); + + rp.dns = evdns_base_new(rp.base, 0); + tt_assert(!evdns_base_nameserver_ip_add(rp.dns, buf)); + + n_replies_left = n_reqs; + + EVTHREAD_ALLOC_LOCK(rp.lock, 0); + EVTHREAD_ALLOC_COND(rp.reqs_cmpl_cond); + EVTHREAD_ALLOC_COND(rp.bw_threads_exited_cond); + tt_assert(rp.lock); + tt_assert(rp.reqs_cmpl_cond); + tt_assert(rp.bw_threads_exited_cond); + rp.bw_threads = 0; + rp.stopping = 0; + + // Run resolver thread + THREAD_START(thread[0], race_base_run, &rp); + // Run busy-wait threads used to force yield this thread + for (i = 1; i < n_threads; i++) { + rp.bw_threads++; + THREAD_START(thread[i], race_busywait_run, &rp); + } + + EVLOCK_LOCK(rp.lock, 0); + rp.locked = 1; + + for (i = 0; i < n_reqs; ++i) { + tt_assert(evdns_getaddrinfo(rp.dns, "foof.example.com", "80", NULL, race_gai_cb, &rp)); + // This magic along with busy-wait threads make this thread yield frequently + if (i % 100 == 0) { + tv.tv_sec = 0; + tv.tv_usec = 10000; + evutil_usleep_(&tv); + } + } + + exit_base = rp.base; + + // Wait for some time + tv.tv_sec = 5; + tv.tv_usec = 0; + EVTHREAD_COND_WAIT_TIMED(rp.reqs_cmpl_cond, rp.lock, &tv); + + // Stop busy-wait threads + tv.tv_sec = 1; + tv.tv_usec = 0; + rp.stopping = 1; + tt_assert(EVTHREAD_COND_WAIT_TIMED(rp.bw_threads_exited_cond, rp.lock, &tv) == 0); + + EVLOCK_UNLOCK(rp.lock, 0); + rp.locked = 0; + + evdns_base_free(rp.dns, 1 /** fail requests */); + + tt_int_op(n_replies_left, ==, 0); + +end: + if (rp.locked) + EVLOCK_UNLOCK(rp.lock, 0); + EVTHREAD_FREE_LOCK(rp.lock, 0); + EVTHREAD_FREE_COND(rp.reqs_cmpl_cond); + EVTHREAD_FREE_COND(rp.bw_threads_exited_cond); + evdns_close_server_port(dns_port); + event_base_loopbreak(rp.base); + event_base_free(rp.base); +} +#endif + +static void +test_set_so_rcvbuf_so_sndbuf(void *arg) +{ + struct basic_test_data *data = arg; + struct evdns_base *dns_base; + + dns_base = evdns_base_new(data->base, 0); + tt_assert(dns_base); + + tt_assert(!evdns_base_set_option(dns_base, "so-rcvbuf", "10240")); + tt_assert(!evdns_base_set_option(dns_base, "so-sndbuf", "10240")); + + /* actually check SO_RCVBUF/SO_SNDBUF not fails */ + tt_assert(!evdns_base_nameserver_ip_add(dns_base, "127.0.0.1")); + +end: + if (dns_base) + evdns_base_free(dns_base, 0); +} + +static void +test_set_option(void *arg) +{ +#define SUCCESS 0 +#define FAIL -1 + struct basic_test_data *data = arg; + struct evdns_base *dns_base; + size_t i; + /* Option names are allowed to have ':' at the end. + * So all test option names come in pairs. + */ + const char *int_options[] = { + "ndots", "ndots:", + "max-timeouts", "max-timeouts:", + "max-inflight", "max-inflight:", + "attempts", "attempts:", + "randomize-case", "randomize-case:", + "so-rcvbuf", "so-rcvbuf:", + "so-sndbuf", "so-sndbuf:", + }; + const char *timeval_options[] = { + "timeout", "timeout:", + "getaddrinfo-allow-skew", "getaddrinfo-allow-skew:", + "initial-probe-timeout", "initial-probe-timeout:", + }; + const char *addr_port_options[] = { + "bind-to", "bind-to:", + }; + + dns_base = evdns_base_new(data->base, 0); + tt_assert(dns_base); + + for (i = 0; i < ARRAY_SIZE(int_options); ++i) { + tt_assert(SUCCESS == evdns_base_set_option(dns_base, int_options[i], "0")); + tt_assert(SUCCESS == evdns_base_set_option(dns_base, int_options[i], "1")); + tt_assert(SUCCESS == evdns_base_set_option(dns_base, int_options[i], "10000")); + tt_assert(FAIL == evdns_base_set_option(dns_base, int_options[i], "foo")); + tt_assert(FAIL == evdns_base_set_option(dns_base, int_options[i], "3.14")); + } + + for (i = 0; i < ARRAY_SIZE(timeval_options); ++i) { + tt_assert(SUCCESS == evdns_base_set_option(dns_base, timeval_options[i], "1")); + tt_assert(SUCCESS == evdns_base_set_option(dns_base, timeval_options[i], "0.001")); + tt_assert(SUCCESS == evdns_base_set_option(dns_base, timeval_options[i], "3.14")); + tt_assert(SUCCESS == evdns_base_set_option(dns_base, timeval_options[i], "10000")); + tt_assert(FAIL == evdns_base_set_option(dns_base, timeval_options[i], "0")); + tt_assert(FAIL == evdns_base_set_option(dns_base, timeval_options[i], "foo")); + } + + for (i = 0; i < ARRAY_SIZE(addr_port_options); ++i) { + tt_assert(SUCCESS == evdns_base_set_option(dns_base, addr_port_options[i], "8.8.8.8:80")); + tt_assert(SUCCESS == evdns_base_set_option(dns_base, addr_port_options[i], "1.2.3.4")); + tt_assert(SUCCESS == evdns_base_set_option(dns_base, addr_port_options[i], "::1:82")); + tt_assert(SUCCESS == evdns_base_set_option(dns_base, addr_port_options[i], "3::4")); + tt_assert(FAIL == evdns_base_set_option(dns_base, addr_port_options[i], "3.14")); + tt_assert(FAIL == evdns_base_set_option(dns_base, addr_port_options[i], "foo")); + } + +#undef SUCCESS +#undef FAIL +end: + if (dns_base) + evdns_base_free(dns_base, 0); +} #define DNS_LEGACY(name, flags) \ { #name, run_legacy_test_fn, flags|TT_LEGACY, &legacy_setup, \ @@ -2138,10 +2461,21 @@ struct testcase_t dns_testcases[] = { { "inflight", dns_inflight_test, TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, { "bufferevent_connect_hostname", test_bufferevent_connect_hostname, TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, +#ifdef EVENT__HAVE_SETRLIMIT + { "bufferevent_connect_hostname_emfile", test_bufferevent_connect_hostname, + TT_FORK|TT_NEED_BASE, &basic_setup, (char*)"emfile" }, +#endif { "disable_when_inactive", dns_disable_when_inactive_test, TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, { "disable_when_inactive_no_ns", dns_disable_when_inactive_no_ns_test, + TT_FORK|TT_NEED_BASE|TT_NO_LOGS, &basic_setup, NULL }, + + { "initialize_nameservers", dns_initialize_nameservers_test, + TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, +#ifndef _WIN32 + { "nameservers_no_default", dns_nameservers_no_default_test, TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, +#endif { "getaddrinfo_async", test_getaddrinfo_async, TT_FORK|TT_NEED_BASE, &basic_setup, (char*)"" }, @@ -2162,9 +2496,21 @@ struct testcase_t dns_testcases[] = { #endif { "client_fail_requests", dns_client_fail_requests_test, - TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + TT_FORK|TT_NEED_BASE|TT_NO_LOGS, &basic_setup, NULL }, + { "client_fail_waiting_requests", dns_client_fail_requests_test, + TT_FORK|TT_NEED_BASE|TT_NO_LOGS, &basic_setup, (char*)"limit-inflight" }, { "client_fail_requests_getaddrinfo", dns_client_fail_requests_getaddrinfo_test, + TT_FORK|TT_NEED_BASE|TT_NO_LOGS, &basic_setup, NULL }, +#ifdef EVTHREAD_USE_PTHREADS_IMPLEMENTED + { "getaddrinfo_race_gotresolve", + getaddrinfo_race_gotresolve_test, + TT_FORK|TT_OFF_BY_DEFAULT, NULL, NULL }, +#endif + + { "set_SO_RCVBUF_SO_SNDBUF", test_set_so_rcvbuf_so_sndbuf, + TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + { "set_options", test_set_option, TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, END_OF_TESTCASES diff --git a/test/regress_et.c b/test/regress_et.c index f75c59b3b7a7..1b1f819eda19 100644 --- a/test/regress_et.c +++ b/test/regress_et.c @@ -51,6 +51,14 @@ static int was_et = 0; +static int base_supports_et(struct event_base *base) +{ + return + (!strcmp(event_base_get_method(base), "epoll") || + !strcmp(event_base_get_method(base), "epoll (with changelist)") || + !strcmp(event_base_get_method(base), "kqueue")); +} + static void read_cb(evutil_socket_t fd, short event, void *arg) { @@ -67,19 +75,14 @@ read_cb(evutil_socket_t fd, short event, void *arg) event_del(arg); } -#ifdef _WIN32 -#define LOCAL_SOCKETPAIR_AF AF_INET -#else -#define LOCAL_SOCKETPAIR_AF AF_UNIX -#endif - static void -test_edgetriggered(void *et) +test_edgetriggered(void *data_) { + struct basic_test_data *data = data_; + struct event_base *base = data->base; + evutil_socket_t *pair = data->pair; struct event *ev = NULL; - struct event_base *base = NULL; const char *test = "test string"; - evutil_socket_t pair[2] = {-1,-1}; int supports_et; /* On Linux 3.2.1 (at least, as patched by Fedora and tested by Nick), @@ -88,39 +91,21 @@ test_edgetriggered(void *et) * get edge-triggered behavior. Yuck! Linux 3.1.9 didn't have this * problem. */ -#ifdef __linux__ - if (evutil_ersatz_socketpair_(AF_INET, SOCK_STREAM, 0, pair) == -1) { - tt_abort_perror("socketpair"); - } -#else - if (evutil_socketpair(LOCAL_SOCKETPAIR_AF, SOCK_STREAM, 0, pair) == -1) { - tt_abort_perror("socketpair"); - } -#endif called = was_et = 0; tt_int_op(send(pair[0], test, (int)strlen(test)+1, 0), >, 0); - shutdown(pair[0], EVUTIL_SHUT_WR); - - /* Initalize the event library */ - base = event_base_new(); - - if (!strcmp(event_base_get_method(base), "epoll") || - !strcmp(event_base_get_method(base), "epoll (with changelist)") || - !strcmp(event_base_get_method(base), "kqueue")) - supports_et = 1; - else - supports_et = 0; + tt_int_op(shutdown(pair[0], EVUTIL_SHUT_WR), ==, 0); + supports_et = base_supports_et(base); TT_BLATHER(("Checking for edge-triggered events with %s, which should %s" "support edge-triggering", event_base_get_method(base), supports_et?"":"not ")); - /* Initalize one event */ + /* Initialize one event */ ev = event_new(base, pair[1], EV_READ|EV_ET|EV_PERSIST, read_cb, &ev); - - event_add(ev, NULL); + tt_assert(ev != NULL); + tt_int_op(event_add(ev, NULL), ==, 0); /* We're going to call the dispatch function twice. The first invocation * will read a single byte from pair[1] in either case. If we're edge @@ -129,8 +114,8 @@ test_edgetriggered(void *et) * do nothing. If we're level triggered, the second invocation of * event_base_loop will also activate the event (because there's still * data to read). */ - event_base_loop(base,EVLOOP_NONBLOCK|EVLOOP_ONCE); - event_base_loop(base,EVLOOP_NONBLOCK|EVLOOP_ONCE); + tt_int_op(event_base_loop(base,EVLOOP_NONBLOCK|EVLOOP_ONCE), ==, 0); + tt_int_op(event_base_loop(base,EVLOOP_NONBLOCK|EVLOOP_ONCE), ==, 0); if (supports_et) { tt_int_op(called, ==, 1); @@ -140,15 +125,11 @@ test_edgetriggered(void *et) tt_assert(!was_et); } - end: +end: if (ev) { event_del(ev); event_free(ev); } - if (base) - event_base_free(base); - evutil_closesocket(pair[0]); - evutil_closesocket(pair[1]); } static void @@ -196,9 +177,94 @@ end: event_base_free(base); } +static int read_notification_count; +static int last_read_notification_was_et; +static void +read_notification_cb(evutil_socket_t fd, short event, void *arg) +{ + read_notification_count++; + last_read_notification_was_et = (event & EV_ET); +} + +static int write_notification_count; +static int last_write_notification_was_et; +static void +write_notification_cb(evutil_socket_t fd, short event, void *arg) +{ + write_notification_count++; + last_write_notification_was_et = (event & EV_ET); +} + +/* After two or more events have been registered for the same + * file descriptor using EV_ET, if one of the events is + * deleted, then the epoll_ctl() call issued by libevent drops + * the EPOLLET flag resulting in level triggered + * notifications. + */ +static void +test_edge_triggered_multiple_events(void *data_) +{ + struct basic_test_data *data = data_; + struct event *read_ev = NULL; + struct event *write_ev = NULL; + const char c = 'A'; + struct event_base *base = data->base; + evutil_socket_t *pair = data->pair; + + if (!base_supports_et(base)) { + tt_skip(); + return; + } + + read_notification_count = 0; + last_read_notification_was_et = 0; + write_notification_count = 0; + last_write_notification_was_et = 0; + + /* Make pair[1] readable */ + tt_int_op(send(pair[0], &c, 1, 0), >, 0); + + read_ev = event_new(base, pair[1], EV_READ|EV_ET|EV_PERSIST, + read_notification_cb, NULL); + write_ev = event_new(base, pair[1], EV_WRITE|EV_ET|EV_PERSIST, + write_notification_cb, NULL); + + event_add(read_ev, NULL); + event_add(write_ev, NULL); + event_base_loop(base, EVLOOP_NONBLOCK|EVLOOP_ONCE); + event_base_loop(base, EVLOOP_NONBLOCK|EVLOOP_ONCE); + + tt_assert(last_read_notification_was_et); + tt_int_op(read_notification_count, ==, 1); + tt_assert(last_write_notification_was_et); + tt_int_op(write_notification_count, ==, 1); + + event_del(read_ev); + + /* trigger acitivity second time for the backend that can have multiple + * events for one fd (like kqueue) */ + close(pair[0]); + pair[0] = -1; + + /* Verify that we are still edge-triggered for write notifications */ + event_base_loop(base, EVLOOP_NONBLOCK|EVLOOP_ONCE); + event_base_loop(base, EVLOOP_NONBLOCK|EVLOOP_ONCE); + tt_assert(last_write_notification_was_et); + tt_int_op(write_notification_count, ==, 2); + +end: + if (read_ev) + event_free(read_ev); + if (write_ev) + event_free(write_ev); +} + struct testcase_t edgetriggered_testcases[] = { - { "et", test_edgetriggered, TT_FORK, NULL, NULL }, + { "et", test_edgetriggered, + TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR, &basic_setup, NULL }, { "et_mix_error", test_edgetriggered_mix_error, TT_FORK|TT_NEED_SOCKETPAIR|TT_NO_LOGS, &basic_setup, NULL }, + { "et_multiple_events", test_edge_triggered_multiple_events, + TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR, &basic_setup, NULL }, END_OF_TESTCASES }; diff --git a/test/regress_finalize.c b/test/regress_finalize.c index 552210fe9d01..9e57188121d1 100644 --- a/test/regress_finalize.c +++ b/test/regress_finalize.c @@ -290,6 +290,53 @@ end: ; } +static void +event_finalize_callback_free(struct event *ev, void *arg) +{ + struct event_base *base = arg; + int err; + if (base) { + err = event_assign(ev, base, -1, EV_TIMEOUT, NULL, NULL); + tt_int_op(err, ==, 0); + test_ok += 1; + } else { + free(ev); + test_ok += 1; + } + +end: + ; +} +static void +test_fin_debug_use_after_free(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct event *ev; + + tt_ptr_op(ev = event_new(base, -1, EV_TIMEOUT, NULL, base), !=, NULL); + tt_int_op(event_add(ev, NULL), ==, 0); + tt_int_op(event_finalize(0, ev, event_finalize_callback_free), ==, 0); + + // Dispatch base to trigger callbacks + event_base_dispatch(base); + event_base_assert_ok_(base); + tt_int_op(test_ok, ==, 1); + + // Now add again, since we did event_assign in event_finalize_callback_free + // This used to fail in event_debug_assert_is_setup_ + tt_int_op(event_add(ev, NULL), ==, 0); + + // Finalize and dispatch again + tt_int_op(event_finalize(0, ev, event_finalize_callback_free), ==, 0); + event_base_dispatch(base); + event_base_assert_ok_(base); + tt_int_op(test_ok, ==, 2); + +end: + ; +} + #if 0 static void timer_callback_3(evutil_socket_t *fd, short what, void *arg) @@ -339,6 +386,7 @@ struct testcase_t finalize_testcases[] = { TEST(cb_invoked, TT_FORK|TT_NEED_BASE), TEST(free_finalize, TT_FORK), TEST(within_cb, TT_FORK|TT_NEED_BASE), + TEST(debug_use_after_free, TT_FORK|TT_NEED_BASE|TT_ENABLE_DEBUG_MODE), // TEST(many, TT_FORK|TT_NEED_BASE), diff --git a/test/regress_http.c b/test/regress_http.c index 80100500e204..4493907163e2 100644 --- a/test/regress_http.c +++ b/test/regress_http.c @@ -66,12 +66,15 @@ #include "regress.h" #include "regress_testutils.h" +#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) + /* set if a test needs to call loopexit on a base */ static struct event_base *exit_base; static char const BASIC_REQUEST_BODY[] = "This is funny"; static void http_basic_cb(struct evhttp_request *req, void *arg); +static void http_timeout_cb(struct evhttp_request *req, void *arg); static void http_large_cb(struct evhttp_request *req, void *arg); static void http_chunked_cb(struct evhttp_request *req, void *arg); static void http_post_cb(struct evhttp_request *req, void *arg); @@ -119,7 +122,7 @@ https_bev(struct event_base *base, void *arg) { SSL *ssl = SSL_new(get_ssl_ctx()); - SSL_use_certificate(ssl, ssl_getcert()); + SSL_use_certificate(ssl, ssl_getcert(ssl_getkey())); SSL_use_PrivateKey(ssl, ssl_getkey()); return bufferevent_openssl_socket_new( @@ -128,7 +131,8 @@ https_bev(struct event_base *base, void *arg) } #endif static struct evhttp * -http_setup(ev_uint16_t *pport, struct event_base *base, int mask) +http_setup_gencb(ev_uint16_t *pport, struct event_base *base, int mask, + void (*cb)(struct evhttp_request *, void *), void *cbarg) { struct evhttp *myhttp; @@ -144,8 +148,12 @@ http_setup(ev_uint16_t *pport, struct event_base *base, int mask) } #endif + evhttp_set_gencb(myhttp, cb, cbarg); + /* Register a callback for certain types of requests */ evhttp_set_cb(myhttp, "/test", http_basic_cb, myhttp); + evhttp_set_cb(myhttp, "/test nonconformant", http_basic_cb, myhttp); + evhttp_set_cb(myhttp, "/timeout", http_timeout_cb, myhttp); evhttp_set_cb(myhttp, "/large", http_large_cb, base); evhttp_set_cb(myhttp, "/chunked", http_chunked_cb, base); evhttp_set_cb(myhttp, "/streamed", http_chunked_cb, base); @@ -159,6 +167,9 @@ http_setup(ev_uint16_t *pport, struct event_base *base, int mask) evhttp_set_cb(myhttp, "/", http_dispatcher_cb, base); return (myhttp); } +static struct evhttp * +http_setup(ev_uint16_t *pport, struct event_base *base, int mask) +{ return http_setup_gencb(pport, base, mask, NULL, NULL); } #ifndef NI_MAXSERV #define NI_MAXSERV 1024 @@ -172,7 +183,7 @@ http_connect(const char *address, ev_uint16_t port) char strport[NI_MAXSERV]; struct sockaddr *sa; - int slen; + size_t slen; evutil_socket_t fd; memset(&ai, 0, sizeof(ai)); @@ -221,6 +232,8 @@ evbuffer_datacmp(struct evbuffer *buf, const char *s) return -1; d = evbuffer_pullup(buf, s_sz); + if (!d) + d = (unsigned char *)""; if ((r = memcmp(d, s, s_sz))) return r; @@ -306,12 +319,31 @@ http_basic_cb(struct evhttp_request *req, void *arg) struct evbuffer *evb = evbuffer_new(); struct evhttp_connection *evcon; int empty = evhttp_find_header(evhttp_request_get_input_headers(req), "Empty") != NULL; - event_debug(("%s: called\n", __func__)); + + TT_BLATHER(("%s: called\n", __func__)); evbuffer_add_printf(evb, BASIC_REQUEST_BODY); evcon = evhttp_request_get_connection(req); tt_assert(evhttp_connection_get_server(evcon) == arg); + { + const struct sockaddr *sa; + char addrbuf[128]; + + sa = evhttp_connection_get_addr(evcon); + tt_assert(sa); + + if (sa->sa_family == AF_INET) { + evutil_format_sockaddr_port_((struct sockaddr *)sa, addrbuf, sizeof(addrbuf)); + tt_assert(!strncmp(addrbuf, "127.0.0.1:", strlen("127.0.0.1:"))); + } else if (sa->sa_family == AF_INET6) { + evutil_format_sockaddr_port_((struct sockaddr *)sa, addrbuf, sizeof(addrbuf)); + tt_assert(!strncmp(addrbuf, "[::1]:", strlen("[::1]:"))); + } else { + tt_fail_msg("Unsupported family"); + } + } + /* For multi-line headers test */ { const char *multi = @@ -346,6 +378,20 @@ end: evbuffer_free(evb); } +static void http_timeout_reply_cb(evutil_socket_t fd, short events, void *arg) +{ + struct evhttp_request *req = arg; + evhttp_send_reply(req, HTTP_OK, "Everything is fine", NULL); + test_ok++; +} +static void +http_timeout_cb(struct evhttp_request *req, void *arg) +{ + struct timeval when = { 0, 100 }; + event_base_once(exit_base, -1, EV_TIMEOUT, + http_timeout_reply_cb, req, &when); +} + static void http_large_cb(struct evhttp_request *req, void *arg) { @@ -396,7 +442,7 @@ http_chunked_cb(struct evhttp_request *req, void *arg) { struct timeval when = { 0, 0 }; struct chunk_req_state *state = malloc(sizeof(struct chunk_req_state)); - event_debug(("%s: called\n", __func__)); + TT_BLATHER(("%s: called\n", __func__)); memset(state, 0, sizeof(struct chunk_req_state)); state->req = req; @@ -414,20 +460,10 @@ http_chunked_cb(struct evhttp_request *req, void *arg) event_base_once(arg, -1, EV_TIMEOUT, http_chunked_trickle_cb, state, &when); } -static void -http_complete_write(evutil_socket_t fd, short what, void *arg) -{ - struct bufferevent *bev = arg; - const char *http_request = "host\r\n" - "Connection: close\r\n" - "\r\n"; - bufferevent_write(bev, http_request, strlen(http_request)); -} - static struct bufferevent * -create_bev(struct event_base *base, int fd, int ssl_mask) +create_bev(struct event_base *base, evutil_socket_t fd, int ssl_mask, int flags_) { - int flags = BEV_OPT_DEFER_CALLBACKS; + int flags = BEV_OPT_DEFER_CALLBACKS | flags_; struct bufferevent *bev = NULL; if (!ssl_mask) { @@ -452,19 +488,34 @@ create_bev(struct event_base *base, int fd, int ssl_mask) } static void -http_basic_test_impl(void *arg, int ssl) +http_half_writecb(struct bufferevent *bev, void *arg) +{ + if (evbuffer_get_length(bufferevent_get_output(bev)) == 0) { + if (!test_ok) { + const char http_request[] = "host\r\n" + "Connection: close\r\n" + "\r\n"; + bufferevent_write(bev, http_request, strlen(http_request)); + } + /* enable reading of the reply */ + bufferevent_enable(bev, EV_READ); + test_ok++; + } +} + +static void +http_basic_test_impl(void *arg, int ssl, const char *request_line) { struct basic_test_data *data = arg; - struct timeval tv; struct bufferevent *bev = NULL; evutil_socket_t fd; const char *http_request; ev_uint16_t port = 0, port2 = 0; int server_flags = ssl ? HTTP_BIND_SSL : 0; struct evhttp *http = http_setup(&port, data->base, server_flags); + struct evbuffer *out; exit_base = data->base; - test_ok = 0; /* bind to a second socket */ if (http_bind(http, &port2, server_flags) == -1) { @@ -475,56 +526,48 @@ http_basic_test_impl(void *arg, int ssl) fd = http_connect("127.0.0.1", port); /* Stupid thing to send a request */ - bev = create_bev(data->base, fd, ssl); - bufferevent_setcb(bev, http_readcb, http_writecb, + bev = create_bev(data->base, fd, ssl, BEV_OPT_CLOSE_ON_FREE); + bufferevent_setcb(bev, http_readcb, http_half_writecb, http_errorcb, data->base); + out = bufferevent_get_output(bev); /* first half of the http request */ - http_request = - "GET /test HTTP/1.1\r\n" - "Host: some"; - - bufferevent_write(bev, http_request, strlen(http_request)); - evutil_timerclear(&tv); - tv.tv_usec = 100000; - event_base_once(data->base, - -1, EV_TIMEOUT, http_complete_write, bev, &tv); + evbuffer_add_printf(out, + "%s\r\n" + "Host: some", request_line); + test_ok = 0; event_base_dispatch(data->base); - - tt_assert(test_ok == 3); + tt_int_op(test_ok, ==, 3); /* connect to the second port */ bufferevent_free(bev); - evutil_closesocket(fd); fd = http_connect("127.0.0.1", port2); /* Stupid thing to send a request */ - bev = create_bev(data->base, fd, ssl); + bev = create_bev(data->base, fd, ssl, BEV_OPT_CLOSE_ON_FREE); bufferevent_setcb(bev, http_readcb, http_writecb, http_errorcb, data->base); + out = bufferevent_get_output(bev); - http_request = - "GET /test HTTP/1.1\r\n" + evbuffer_add_printf(out, + "%s\r\n" "Host: somehost\r\n" "Connection: close\r\n" - "\r\n"; - - bufferevent_write(bev, http_request, strlen(http_request)); + "\r\n", request_line); + test_ok = 0; event_base_dispatch(data->base); - - tt_assert(test_ok == 5); + tt_int_op(test_ok, ==, 2); /* Connect to the second port again. This time, send an absolute uri. */ bufferevent_free(bev); - evutil_closesocket(fd); fd = http_connect("127.0.0.1", port2); /* Stupid thing to send a request */ - bev = create_bev(data->base, fd, ssl); + bev = create_bev(data->base, fd, ssl, BEV_OPT_CLOSE_ON_FREE); bufferevent_setcb(bev, http_readcb, http_writecb, http_errorcb, data->base); @@ -536,17 +579,19 @@ http_basic_test_impl(void *arg, int ssl) bufferevent_write(bev, http_request, strlen(http_request)); + test_ok = 0; event_base_dispatch(data->base); - - tt_assert(test_ok == 7); + tt_int_op(test_ok, ==, 2); evhttp_free(http); - end: +end: if (bev) bufferevent_free(bev); } -static void http_basic_test(void *arg) -{ return http_basic_test_impl(arg, 0); } +static void http_basic_test(void *arg)\ +{ http_basic_test_impl(arg, 0, "GET /test HTTP/1.1"); } +static void http_basic_trailing_space_test(void *arg) +{ http_basic_test_impl(arg, 0, "GET /test HTTP/1.1 "); } static void @@ -585,7 +630,7 @@ http_badreq_cb(struct evhttp_request *req, void *arg) static void http_badreq_errorcb(struct bufferevent *bev, short what, void *arg) { - event_debug(("%s: called (what=%04x, arg=%p)", __func__, what, arg)); + TT_BLATHER(("%s: called (what=%04x, arg=%p)", __func__, what, arg)); /* ignore */ } @@ -631,7 +676,7 @@ http_badreq_readcb(struct bufferevent *bev, void *arg) static void http_badreq_successcb(evutil_socket_t fd, short what, void *arg) { - event_debug(("%s: called (what=%04x, arg=%p)", __func__, what, arg)); + TT_BLATHER(("%s: called (what=%04x, arg=%p)", __func__, what, arg)); event_base_loopexit(exit_base, NULL); } @@ -641,7 +686,7 @@ http_bad_request_test(void *arg) struct basic_test_data *data = arg; struct timeval tv; struct bufferevent *bev = NULL; - evutil_socket_t fd = -1; + evutil_socket_t fd = EVUTIL_INVALID_SOCKET; const char *http_request; ev_uint16_t port=0, port2=0; struct evhttp *http = http_setup(&port, data->base, 0); @@ -655,7 +700,7 @@ http_bad_request_test(void *arg) /* NULL request test */ fd = http_connect("127.0.0.1", port); - tt_int_op(fd, >=, 0); + tt_assert(fd != EVUTIL_INVALID_SOCKET); /* Stupid thing to send a request */ bev = bufferevent_socket_new(data->base, fd, 0); @@ -687,6 +732,7 @@ http_bad_request_test(void *arg) /* connect to the second port */ fd = http_connect("127.0.0.1", port2); + tt_assert(fd != EVUTIL_INVALID_SOCKET); /* Stupid thing to send a request */ bev = bufferevent_socket_new(data->base, fd, 0); @@ -747,7 +793,7 @@ http_delete_cb(struct evhttp_request *req, void *arg) exit(1); } - event_debug(("%s: called\n", __func__)); + TT_BLATHER(("%s: called\n", __func__)); evbuffer_add_printf(evb, BASIC_REQUEST_BODY); /* allow sending of an empty reply */ @@ -762,7 +808,7 @@ http_delete_test(void *arg) { struct basic_test_data *data = arg; struct bufferevent *bev; - evutil_socket_t fd = -1; + evutil_socket_t fd = EVUTIL_INVALID_SOCKET; const char *http_request; ev_uint16_t port = 0; struct evhttp *http = http_setup(&port, data->base, 0); @@ -772,7 +818,7 @@ http_delete_test(void *arg) tt_assert(http); fd = http_connect("127.0.0.1", port); - tt_int_op(fd, >=, 0); + tt_assert(fd != EVUTIL_INVALID_SOCKET); /* Stupid thing to send a request */ bev = bufferevent_socket_new(data->base, fd, 0); @@ -791,7 +837,7 @@ http_delete_test(void *arg) bufferevent_free(bev); evutil_closesocket(fd); - fd = -1; + fd = EVUTIL_INVALID_SOCKET; evhttp_free(http); @@ -818,7 +864,7 @@ http_sent_cb(struct evhttp_request *req, void *arg) exit(1); } - event_debug(("%s: called\n", __func__)); + TT_BLATHER(("%s: called\n", __func__)); ++test_ok; } @@ -830,7 +876,7 @@ http_on_complete_cb(struct evhttp_request *req, void *arg) evhttp_request_set_on_complete_cb(req, http_sent_cb, (void *)0xDEADBEEF); - event_debug(("%s: called\n", __func__)); + TT_BLATHER(("%s: called\n", __func__)); evbuffer_add_printf(evb, BASIC_REQUEST_BODY); /* allow sending of an empty reply */ @@ -846,7 +892,7 @@ http_on_complete_test(void *arg) { struct basic_test_data *data = arg; struct bufferevent *bev; - evutil_socket_t fd = -1; + evutil_socket_t fd = EVUTIL_INVALID_SOCKET; const char *http_request; ev_uint16_t port = 0; struct evhttp *http = http_setup(&port, data->base, 0); @@ -855,7 +901,7 @@ http_on_complete_test(void *arg) test_ok = 0; fd = http_connect("127.0.0.1", port); - tt_int_op(fd, >=, 0); + tt_assert(fd != EVUTIL_INVALID_SOCKET); /* Stupid thing to send a request */ bev = bufferevent_socket_new(data->base, fd, 0); @@ -916,7 +962,7 @@ http_allowed_methods_test(void *arg) test_ok = 0; fd1 = http_connect("127.0.0.1", port); - tt_int_op(fd1, >=, 0); + tt_assert(fd1 != EVUTIL_INVALID_SOCKET); /* GET is out; PATCH is in. */ evhttp_set_allowed_methods(http, EVHTTP_REQ_PATCH); @@ -938,7 +984,7 @@ http_allowed_methods_test(void *arg) event_base_dispatch(data->base); fd2 = http_connect("127.0.0.1", port); - tt_int_op(fd2, >=, 0); + tt_assert(fd2 != EVUTIL_INVALID_SOCKET); bev2 = bufferevent_socket_new(data->base, fd2, 0); bufferevent_enable(bev2, EV_READ|EV_WRITE); @@ -956,7 +1002,7 @@ http_allowed_methods_test(void *arg) event_base_dispatch(data->base); fd3 = http_connect("127.0.0.1", port); - tt_int_op(fd3, >=, 0); + tt_assert(fd3 != EVUTIL_INVALID_SOCKET); bev3 = bufferevent_socket_new(data->base, fd3, 0); bufferevent_enable(bev3, EV_READ|EV_WRITE); @@ -1239,6 +1285,7 @@ http_autofree_connection_test(void *arg) struct evhttp_connection *evcon = NULL; struct evhttp_request *req[2] = { NULL }; struct evhttp *http = http_setup(&port, data->base, 0); + size_t i; test_ok = 0; @@ -1253,19 +1300,14 @@ http_autofree_connection_test(void *arg) req[1] = evhttp_request_new(http_request_empty_done, data->base); /* Add the information that we care about */ - evhttp_add_header(evhttp_request_get_output_headers(req[0]), "Host", "somehost"); - evhttp_add_header(evhttp_request_get_output_headers(req[0]), "Connection", "close"); - evhttp_add_header(evhttp_request_get_output_headers(req[0]), "Empty", "itis"); - evhttp_add_header(evhttp_request_get_output_headers(req[1]), "Host", "somehost"); - evhttp_add_header(evhttp_request_get_output_headers(req[1]), "Connection", "close"); - evhttp_add_header(evhttp_request_get_output_headers(req[1]), "Empty", "itis"); + for (i = 0; i < ARRAY_SIZE(req); ++i) { + evhttp_add_header(evhttp_request_get_output_headers(req[i]), "Host", "somehost"); + evhttp_add_header(evhttp_request_get_output_headers(req[i]), "Connection", "close"); + evhttp_add_header(evhttp_request_get_output_headers(req[i]), "Empty", "itis"); - /* We give ownership of the request to the connection */ - if (evhttp_make_request(evcon, req[0], EVHTTP_REQ_GET, "/test") == -1) { - tt_abort_msg("couldn't make request"); - } - if (evhttp_make_request(evcon, req[1], EVHTTP_REQ_GET, "/test") == -1) { - tt_abort_msg("couldn't make request"); + if (evhttp_make_request(evcon, req[i], EVHTTP_REQ_GET, "/test") == -1) { + tt_abort_msg("couldn't make request"); + } } /* @@ -1276,7 +1318,8 @@ http_autofree_connection_test(void *arg) evhttp_connection_free_on_completion(evcon); evcon = NULL; - event_base_dispatch(data->base); + for (i = 0; i < ARRAY_SIZE(req); ++i) + event_base_dispatch(data->base); /* at this point, the http server should have no connection */ tt_assert(TAILQ_FIRST(&http->connections) == NULL); @@ -1744,6 +1787,7 @@ http_virtual_host_test(void *arg) /* Now make a raw request with an absolute URI. */ fd = http_connect("127.0.0.1", port); + tt_assert(fd != EVUTIL_INVALID_SOCKET); /* Stupid thing to send a request */ bev = bufferevent_socket_new(data->base, fd, 0); @@ -1825,7 +1869,7 @@ http_dispatcher_cb(struct evhttp_request *req, void *arg) { struct evbuffer *evb = evbuffer_new(); - event_debug(("%s: called\n", __func__)); + TT_BLATHER(("%s: called\n", __func__)); evbuffer_add_printf(evb, "DISPATCHER_TEST"); evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); @@ -1983,7 +2027,7 @@ void http_post_cb(struct evhttp_request *req, void *arg) { struct evbuffer *evb; - event_debug(("%s: called\n", __func__)); + TT_BLATHER(("%s: called\n", __func__)); /* Yes, we are expecting a post request */ if (evhttp_request_get_command(req) != EVHTTP_REQ_POST) { @@ -2100,7 +2144,7 @@ void http_put_cb(struct evhttp_request *req, void *arg) { struct evbuffer *evb; - event_debug(("%s: called\n", __func__)); + TT_BLATHER(("%s: called\n", __func__)); /* Expecting a PUT request */ if (evhttp_request_get_command(req) != EVHTTP_REQ_PUT) { @@ -2186,7 +2230,7 @@ http_failure_test(void *arg) { struct basic_test_data *data = arg; struct bufferevent *bev; - evutil_socket_t fd = -1; + evutil_socket_t fd = EVUTIL_INVALID_SOCKET; const char *http_request; ev_uint16_t port = 0; struct evhttp *http = http_setup(&port, data->base, 0); @@ -2194,7 +2238,7 @@ http_failure_test(void *arg) test_ok = 0; fd = http_connect("127.0.0.1", port); - tt_int_op(fd, >=, 0); + tt_assert(fd != EVUTIL_INVALID_SOCKET); /* Stupid thing to send a request */ bev = bufferevent_socket_new(data->base, fd, 0); @@ -2453,6 +2497,27 @@ http_parse_query_test(void *ptr) end: evhttp_clear_headers(&headers); } +static void +http_parse_query_str_test(void *ptr) +{ + struct evkeyvalq headers; + int r; + + TAILQ_INIT(&headers); + + r = evhttp_parse_query_str("http://www.test.com/?q=test", &headers); + tt_assert(evhttp_find_header(&headers, "q") == NULL); + tt_int_op(r, ==, 0); + evhttp_clear_headers(&headers); + + r = evhttp_parse_query_str("q=test", &headers); + tt_want(validate_header(&headers, "q", "test") == 0); + tt_int_op(r, ==, 0); + evhttp_clear_headers(&headers); + +end: + evhttp_clear_headers(&headers); +} static void http_parse_uri_test(void *ptr) @@ -2963,7 +3028,7 @@ http_base_test(void *ptr) http = http_setup(&port, base, 0); fd = http_connect("127.0.0.1", port); - tt_int_op(fd, >=, 0); + tt_assert(fd != EVUTIL_INVALID_SOCKET); /* Stupid thing to send a request */ bev = bufferevent_socket_new(base, fd, 0); @@ -3050,10 +3115,10 @@ http_incomplete_test_(struct basic_test_data *data, int use_timeout, int ssl) evhttp_set_timeout(http, 1); fd = http_connect("127.0.0.1", port); - tt_int_op(fd, >=, 0); + tt_assert(fd != EVUTIL_INVALID_SOCKET); /* Stupid thing to send a request */ - bev = create_bev(data->base, fd, ssl); + bev = create_bev(data->base, fd, ssl, 0); bufferevent_setcb(bev, http_incomplete_readcb, http_incomplete_writecb, http_incomplete_errorcb, use_timeout ? NULL : &fd); @@ -3074,7 +3139,7 @@ http_incomplete_test_(struct basic_test_data *data, int use_timeout, int ssl) bufferevent_free(bev); if (use_timeout) { evutil_closesocket(fd); - fd = -1; + fd = EVUTIL_INVALID_SOCKET; } evhttp_free(http); @@ -3253,7 +3318,7 @@ static void http_chunk_out_test_impl(void *arg, int ssl) { struct basic_test_data *data = arg; - struct bufferevent *bev; + struct bufferevent *bev = NULL; evutil_socket_t fd; const char *http_request; ev_uint16_t port = 0; @@ -3267,9 +3332,10 @@ http_chunk_out_test_impl(void *arg, int ssl) test_ok = 0; fd = http_connect("127.0.0.1", port); + tt_assert(fd != EVUTIL_INVALID_SOCKET); /* Stupid thing to send a request */ - bev = create_bev(data->base, fd, ssl); + bev = create_bev(data->base, fd, ssl, BEV_OPT_CLOSE_ON_FREE); bufferevent_setcb(bev, http_chunked_readcb, http_chunked_writecb, http_chunked_errorcb, data->base); @@ -3287,6 +3353,7 @@ http_chunk_out_test_impl(void *arg, int ssl) event_base_dispatch(data->base); bufferevent_free(bev); + bev = NULL; evutil_gettimeofday(&tv_end, NULL); evutil_timersub(&tv_end, &tv_start, &tv_end); @@ -3296,7 +3363,7 @@ http_chunk_out_test_impl(void *arg, int ssl) tt_int_op(test_ok, ==, 2); /* now try again with the regular connection object */ - bev = create_bev(data->base, -1, ssl); + bev = create_bev(data->base, -1, ssl, BEV_OPT_CLOSE_ON_FREE); evcon = evhttp_connection_base_bufferevent_new( data->base, NULL, bev, "127.0.0.1", port); tt_assert(evcon); @@ -3304,14 +3371,13 @@ http_chunk_out_test_impl(void *arg, int ssl) /* make two requests to check the keepalive behavior */ for (i = 0; i < 2; i++) { test_ok = 0; - req = evhttp_request_new(http_chunked_request_done,data->base); + req = evhttp_request_new(http_chunked_request_done, data->base); /* Add the information that we care about */ evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); /* We give ownership of the request to the connection */ - if (evhttp_make_request(evcon, req, - EVHTTP_REQ_GET, "/chunked") == -1) { + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/chunked") == -1) { tt_abort_msg("Couldn't make request"); } @@ -3327,7 +3393,7 @@ http_chunk_out_test_impl(void *arg, int ssl) evhttp_free(http); } static void http_chunk_out_test(void *arg) -{ return http_chunk_out_test_impl(arg, 0); } +{ http_chunk_out_test_impl(arg, 0); } static void http_stream_out_test_impl(void *arg, int ssl) @@ -3342,7 +3408,7 @@ http_stream_out_test_impl(void *arg, int ssl) test_ok = 0; exit_base = data->base; - bev = create_bev(data->base, -1, ssl); + bev = create_bev(data->base, -1, ssl, 0); evcon = evhttp_connection_base_bufferevent_new( data->base, NULL, bev, "127.0.0.1", port); tt_assert(evcon); @@ -3373,7 +3439,7 @@ http_stream_out_test_impl(void *arg, int ssl) evhttp_free(http); } static void http_stream_out_test(void *arg) -{ return http_stream_out_test_impl(arg, 0); } +{ http_stream_out_test_impl(arg, 0); } static void http_stream_in_chunk(struct evhttp_request *req, void *arg) @@ -3542,7 +3608,7 @@ http_connection_fail_test_impl(void *arg, int ssl) /* auto detect a port */ evhttp_free(http); - bev = create_bev(data->base, -1, ssl); + bev = create_bev(data->base, -1, ssl, 0); /* Pick an unroutable address. This administratively scoped multicast * address should do when working with TCP. */ evcon = evhttp_connection_base_bufferevent_new( @@ -3569,7 +3635,7 @@ http_connection_fail_test_impl(void *arg, int ssl) ; } static void http_connection_fail_test(void *arg) -{ return http_connection_fail_test_impl(arg, 0); } +{ http_connection_fail_test_impl(arg, 0); } static void http_connection_retry_done(struct evhttp_request *req, void *arg) @@ -3602,19 +3668,19 @@ http_make_web_server(evutil_socket_t fd, short what, void *arg) } static void -http_simple_test_impl(void *arg, int ssl, int dirty) +http_simple_test_impl(void *arg, int ssl, int dirty, const char *uri) { struct basic_test_data *data = arg; struct evhttp_connection *evcon = NULL; struct evhttp_request *req = NULL; struct bufferevent *bev; - struct http_server hs = { .port = 0, .ssl = ssl, }; + struct http_server hs = { 0, ssl, NULL, }; struct evhttp *http = http_setup(&hs.port, data->base, ssl ? HTTP_BIND_SSL : 0); exit_base = data->base; test_ok = 0; - bev = create_bev(data->base, -1, ssl); + bev = create_bev(data->base, -1, ssl, 0); #ifdef EVENT__HAVE_OPENSSL bufferevent_openssl_set_allow_dirty_shutdown(bev, dirty); #endif @@ -3627,9 +3693,8 @@ http_simple_test_impl(void *arg, int ssl, int dirty) req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); tt_assert(req); - if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, uri) == -1) tt_abort_msg("Couldn't make request"); - } event_base_dispatch(data->base); tt_int_op(test_ok, ==, 1); @@ -3641,7 +3706,9 @@ http_simple_test_impl(void *arg, int ssl, int dirty) evhttp_free(http); } static void http_simple_test(void *arg) -{ return http_simple_test_impl(arg, 0, 0); } +{ http_simple_test_impl(arg, 0, 0, "/test"); } +static void http_simple_nonconformant_test(void *arg) +{ http_simple_test_impl(arg, 0, 0, "/test nonconformant"); } static void http_connection_retry_test_basic(void *arg, const char *addr, struct evdns_base *dns_base, int ssl) @@ -3651,7 +3718,7 @@ http_connection_retry_test_basic(void *arg, const char *addr, struct evdns_base struct evhttp_request *req = NULL; struct timeval tv, tv_start, tv_end; struct bufferevent *bev; - struct http_server hs = { .port = 0, .ssl = ssl, }; + struct http_server hs = { 0, ssl, NULL, }; struct evhttp *http = http_setup(&hs.port, data->base, ssl ? HTTP_BIND_SSL : 0); exit_base = data->base; @@ -3660,7 +3727,7 @@ http_connection_retry_test_basic(void *arg, const char *addr, struct evdns_base /* auto detect a port */ evhttp_free(http); - bev = create_bev(data->base, -1, ssl); + bev = create_bev(data->base, -1, ssl, 0); evcon = evhttp_connection_base_bufferevent_new(data->base, dns_base, bev, addr, hs.port); tt_assert(evcon); if (dns_base) @@ -3798,16 +3865,16 @@ http_connection_retry_conn_address_test_impl(void *arg, int ssl) /** dnsserver will be cleaned in http_connection_retry_test_basic() */ } static void http_connection_retry_conn_address_test(void *arg) -{ return http_connection_retry_conn_address_test_impl(arg, 0); } +{ http_connection_retry_conn_address_test_impl(arg, 0); } static void http_connection_retry_test_impl(void *arg, int ssl) { - return http_connection_retry_test_basic(arg, "127.0.0.1", NULL, ssl); + http_connection_retry_test_basic(arg, "127.0.0.1", NULL, ssl); } static void http_connection_retry_test(void *arg) -{ return http_connection_retry_test_impl(arg, 0); } +{ http_connection_retry_test_impl(arg, 0); } static void http_primitives(void *ptr) @@ -3844,7 +3911,7 @@ http_multi_line_header_test(void *arg) { struct basic_test_data *data = arg; struct bufferevent *bev= NULL; - evutil_socket_t fd = -1; + evutil_socket_t fd = EVUTIL_INVALID_SOCKET; const char *http_start_request; ev_uint16_t port = 0; struct evhttp *http = http_setup(&port, data->base, 0); @@ -3855,8 +3922,7 @@ http_multi_line_header_test(void *arg) tt_ptr_op(http, !=, NULL); fd = http_connect("127.0.0.1", port); - - tt_int_op(fd, !=, -1); + tt_assert(fd != EVUTIL_INVALID_SOCKET); /* Stupid thing to send a request */ bev = bufferevent_socket_new(data->base, fd, 0); @@ -3960,7 +4026,6 @@ http_large_entity_test_done(struct evhttp_request *req, void *arg) end: event_base_loopexit(arg, NULL); } -#ifndef WIN32 static void http_expectation_failed_done(struct evhttp_request *req, void *arg) { @@ -3969,7 +4034,6 @@ http_expectation_failed_done(struct evhttp_request *req, void *arg) end: event_base_loopexit(arg, NULL); } -#endif static void http_data_length_constraints_test_impl(void *arg, int read_on_write_error) @@ -3986,38 +4050,32 @@ http_data_length_constraints_test_impl(void *arg, int read_on_write_error) test_ok = 0; cb = http_failed_request_done; -#ifndef WIN32 if (read_on_write_error) cb = http_data_length_constraints_test_done; -#endif tt_assert(continue_size < size); + long_str = malloc(size); + memset(long_str, 'a', size); + long_str[size - 1] = '\0'; + + TT_BLATHER(("Creating connection to :%i", port)); evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); tt_assert(evcon); if (read_on_write_error) tt_assert(!evhttp_connection_set_flags(evcon, EVHTTP_CON_READ_ON_WRITE_ERROR)); - /* also bind to local host */ evhttp_connection_set_local_address(evcon, "127.0.0.1"); - /* - * At this point, we want to schedule an HTTP GET request - * server using our make request method. - */ + evhttp_set_max_headers_size(http, size - 1); + TT_BLATHER(("Set max header size %zu", size - 1)); req = evhttp_request_new(http_data_length_constraints_test_done, data->base); tt_assert(req); - - long_str = malloc(size); - memset(long_str, 'a', size); - long_str[size - 1] = '\0'; - /* Add the information that we care about */ - evhttp_set_max_headers_size(http, size - 1); evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); evhttp_add_header(evhttp_request_get_output_headers(req), "Longheader", long_str); - + TT_BLATHER(("GET /?arg=val")); if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/?arg=val") == -1) { tt_abort_msg("Couldn't make request"); } @@ -4026,21 +4084,22 @@ http_data_length_constraints_test_impl(void *arg, int read_on_write_error) req = evhttp_request_new(http_data_length_constraints_test_done, data->base); tt_assert(req); evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); - /* GET /?arg=verylongvalue HTTP/1.1 */ + TT_BLATHER(("GET %s", long_str)); if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, long_str) == -1) { tt_abort_msg("Couldn't make request"); } event_base_dispatch(data->base); -#ifndef WIN32 + evhttp_set_max_body_size(http, size - 2); + TT_BLATHER(("Set body header size %zu", size - 2)); + if (read_on_write_error) cb = http_large_entity_test_done; -#endif - evhttp_set_max_body_size(http, size - 2); req = evhttp_request_new(cb, data->base); evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); evbuffer_add_printf(evhttp_request_get_output_buffer(req), "%s", long_str); + TT_BLATHER(("POST /")); if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/") == -1) { tt_abort_msg("Couldn't make request"); } @@ -4050,29 +4109,31 @@ http_data_length_constraints_test_impl(void *arg, int read_on_write_error) evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); evhttp_add_header(evhttp_request_get_output_headers(req), "Expect", "100-continue"); evbuffer_add_printf(evhttp_request_get_output_buffer(req), "%s", long_str); + TT_BLATHER(("POST / (Expect: 100-continue, http_large_entity_test_done)")); if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/") == -1) { tt_abort_msg("Couldn't make request"); } event_base_dispatch(data->base); + long_str[continue_size] = '\0'; + req = evhttp_request_new(http_dispatcher_test_done, data->base); evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); evhttp_add_header(evhttp_request_get_output_headers(req), "Expect", "100-continue"); - long_str[continue_size] = '\0'; evbuffer_add_printf(evhttp_request_get_output_buffer(req), "%s", long_str); + TT_BLATHER(("POST / (Expect: 100-continue, http_dispatcher_test_done)")); if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/") == -1) { tt_abort_msg("Couldn't make request"); } event_base_dispatch(data->base); -#ifndef WIN32 if (read_on_write_error) cb = http_expectation_failed_done; -#endif req = evhttp_request_new(cb, data->base); evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); evhttp_add_header(evhttp_request_get_output_headers(req), "Expect", "101-continue"); evbuffer_add_printf(evhttp_request_get_output_buffer(req), "%s", long_str); + TT_BLATHER(("POST / (Expect: 101-continue)")); if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/") == -1) { tt_abort_msg("Couldn't make request"); } @@ -4253,7 +4314,7 @@ http_terminate_chunked_test_impl(void *arg, int oneshot) struct timeval tv; const char *http_request; ev_uint16_t port = 0; - evutil_socket_t fd = -1; + evutil_socket_t fd = EVUTIL_INVALID_SOCKET; struct terminate_state terminate_state; struct evhttp *http = http_setup(&port, data->base, 0); @@ -4264,6 +4325,7 @@ http_terminate_chunked_test_impl(void *arg, int oneshot) terminate_chunked_cb, &terminate_state) == 0); fd = http_connect("127.0.0.1", port); + tt_assert(fd != EVUTIL_INVALID_SOCKET); /* Stupid thing to send a request */ bev = bufferevent_socket_new(data->base, fd, 0); @@ -4444,7 +4506,7 @@ http_write_during_read_test_impl(void *arg, int ssl) ev_uint16_t port = 0; struct bufferevent *bev = NULL; struct timeval tv; - int fd; + evutil_socket_t fd; const char *http_request; struct evhttp *http = http_setup(&port, data->base, ssl ? HTTP_BIND_SSL : 0); @@ -4452,7 +4514,8 @@ http_write_during_read_test_impl(void *arg, int ssl) exit_base = data->base; fd = http_connect("127.0.0.1", port); - bev = create_bev(data->base, fd, 0); + tt_assert(fd != EVUTIL_INVALID_SOCKET); + bev = create_bev(data->base, fd, 0, 0); bufferevent_setcb(bev, NULL, NULL, NULL, data->base); bufferevent_disable(bev, EV_READ); @@ -4468,13 +4531,14 @@ http_write_during_read_test_impl(void *arg, int ssl) event_base_dispatch(data->base); +end: if (bev) bufferevent_free(bev); if (http) evhttp_free(http); } static void http_write_during_read_test(void *arg) -{ return http_write_during_read_test_impl(arg, 0); } +{ http_write_during_read_test_impl(arg, 0); } static void http_request_own_test(void *arg) @@ -4511,6 +4575,81 @@ http_request_own_test(void *arg) test_ok = 1; } +static void http_run_bev_request(struct event_base *base, int port, + const char *fmt, ...) +{ + struct bufferevent *bev = NULL; + va_list ap; + evutil_socket_t fd; + struct evbuffer *out; + + fd = http_connect("127.0.0.1", port); + tt_assert(fd != EVUTIL_INVALID_SOCKET); + + /* Stupid thing to send a request */ + bev = create_bev(base, fd, 0, 0); + bufferevent_setcb(bev, http_readcb, http_writecb, + http_errorcb, base); + out = bufferevent_get_output(bev); + + va_start(ap, fmt); + evbuffer_add_vprintf(out, fmt, ap); + va_end(ap); + + event_base_dispatch(base); + +end: + if (bev) + bufferevent_free(bev); +} +static void +http_request_extra_body_test(void *arg) +{ + struct basic_test_data *data = arg; + struct bufferevent *bev = NULL; + ev_uint16_t port = 0; + int i; + struct evhttp *http = + http_setup_gencb(&port, data->base, 0, http_timeout_cb, NULL); + struct evbuffer *body = NULL; + + exit_base = data->base; + test_ok = 0; + + body = evbuffer_new(); + for (i = 0; i < 10000; ++i) + evbuffer_add_printf(body, "this is the body that HEAD should not have"); + + http_run_bev_request(data->base, port, + "HEAD /timeout HTTP/1.1\r\n" + "Host: somehost\r\n" + "Connection: close\r\n" + "Content-Length: %i\r\n" + "\r\n%s", + (int)evbuffer_get_length(body), + evbuffer_pullup(body, -1) + ); + tt_assert(test_ok == -2); + + http_run_bev_request(data->base, port, + "HEAD /__gencb__ HTTP/1.1\r\n" + "Host: somehost\r\n" + "Connection: close\r\n" + "Content-Length: %i\r\n" + "\r\n%s", + (int)evbuffer_get_length(body), + evbuffer_pullup(body, -1) + ); + tt_assert(test_ok == -2); + + end: + evhttp_free(http); + if (bev) + bufferevent_free(bev); + if (body) + evbuffer_free(body); +} + #define HTTP_LEGACY(name) \ { #name, run_legacy_test_fn, TT_ISOLATED|TT_LEGACY, &legacy_setup, \ http_##name##_test } @@ -4518,43 +4657,45 @@ http_request_own_test(void *arg) #define HTTP_CAST_ARG(a) ((void *)(a)) #define HTTP_OFF_N(title, name, arg) \ { #title, http_##name##_test, TT_ISOLATED|TT_OFF_BY_DEFAULT, &basic_setup, HTTP_CAST_ARG(arg) } -#define HTTP_N(title, name, arg) \ - { #title, http_##name##_test, TT_ISOLATED, &basic_setup, HTTP_CAST_ARG(arg) } -#define HTTP(name) HTTP_N(name, name, NULL) +#define HTTP_RET_N(title, name, test_opts, arg) \ + { #title, http_##name##_test, TT_ISOLATED|TT_RETRIABLE|test_opts, &basic_setup, HTTP_CAST_ARG(arg) } +#define HTTP_N(title, name, test_opts, arg) \ + { #title, http_##name##_test, TT_ISOLATED|test_opts, &basic_setup, HTTP_CAST_ARG(arg) } +#define HTTP(name) HTTP_N(name, name, 0, NULL) #define HTTPS(name) \ { "https_" #name, https_##name##_test, TT_ISOLATED, &basic_setup, NULL } #ifdef EVENT__HAVE_OPENSSL static void https_basic_test(void *arg) -{ return http_basic_test_impl(arg, 1); } +{ http_basic_test_impl(arg, 1, "GET /test HTTP/1.1"); } static void https_filter_basic_test(void *arg) -{ return http_basic_test_impl(arg, 1 | HTTP_SSL_FILTER); } +{ http_basic_test_impl(arg, 1 | HTTP_SSL_FILTER, "GET /test HTTP/1.1"); } static void https_incomplete_test(void *arg) { http_incomplete_test_(arg, 0, 1); } static void https_incomplete_timeout_test(void *arg) { http_incomplete_test_(arg, 1, 1); } static void https_simple_test(void *arg) -{ return http_simple_test_impl(arg, 1, 0); } +{ http_simple_test_impl(arg, 1, 0, "/test"); } static void https_simple_dirty_test(void *arg) -{ return http_simple_test_impl(arg, 1, 1); } +{ http_simple_test_impl(arg, 1, 1, "/test"); } static void https_connection_retry_conn_address_test(void *arg) -{ return http_connection_retry_conn_address_test_impl(arg, 1); } +{ http_connection_retry_conn_address_test_impl(arg, 1); } static void https_connection_retry_test(void *arg) -{ return http_connection_retry_test_impl(arg, 1); } +{ http_connection_retry_test_impl(arg, 1); } static void https_chunk_out_test(void *arg) -{ return http_chunk_out_test_impl(arg, 1); } +{ http_chunk_out_test_impl(arg, 1); } static void https_filter_chunk_out_test(void *arg) -{ return http_chunk_out_test_impl(arg, 1 | HTTP_SSL_FILTER); } +{ http_chunk_out_test_impl(arg, 1 | HTTP_SSL_FILTER); } static void https_stream_out_test(void *arg) -{ return http_stream_out_test_impl(arg, 1); } +{ http_stream_out_test_impl(arg, 1); } static void https_connection_fail_test(void *arg) -{ return http_connection_fail_test_impl(arg, 1); } +{ http_connection_fail_test_impl(arg, 1); } static void https_write_during_read_test(void *arg) -{ return http_write_during_read_test_impl(arg, 1); } +{ http_write_during_read_test_impl(arg, 1); } static void https_connection_test(void *arg) -{ return http_connection_test_(arg, 0, "127.0.0.1", NULL, 0, AF_UNSPEC, 1); } +{ http_connection_test_(arg, 0, "127.0.0.1", NULL, 0, AF_UNSPEC, 1); } static void https_persist_connection_test(void *arg) -{ return http_connection_test_(arg, 1, "127.0.0.1", NULL, 0, AF_UNSPEC, 1); } +{ http_connection_test_(arg, 1, "127.0.0.1", NULL, 0, AF_UNSPEC, 1); } #endif struct testcase_t http_testcases[] = { @@ -4562,24 +4703,27 @@ struct testcase_t http_testcases[] = { { "base", http_base_test, TT_FORK, NULL, NULL }, { "bad_headers", http_bad_header_test, 0, NULL, NULL }, { "parse_query", http_parse_query_test, 0, NULL, NULL }, + { "parse_query_str", http_parse_query_str_test, 0, NULL, NULL }, { "parse_uri", http_parse_uri_test, 0, NULL, NULL }, { "parse_uri_nc", http_parse_uri_test, 0, &basic_setup, (void*)"nc" }, { "uriencode", http_uriencode_test, 0, NULL, NULL }, HTTP(basic), + HTTP(basic_trailing_space), HTTP(simple), - - HTTP_N(cancel, cancel, BASIC), - HTTP_N(cancel_by_host, cancel, BY_HOST), - HTTP_N(cancel_by_host_no_ns, cancel, BY_HOST | NO_NS), - HTTP_N(cancel_by_host_inactive_server, cancel, BY_HOST | INACTIVE_SERVER), - HTTP_N(cancel_inactive_server, cancel, INACTIVE_SERVER), - HTTP_N(cancel_by_host_no_ns_inactive_server, cancel, BY_HOST | NO_NS | INACTIVE_SERVER), + HTTP(simple_nonconformant), + + HTTP_N(cancel, cancel, 0, BASIC), + HTTP_RET_N(cancel_by_host, cancel, 0, BY_HOST), + HTTP_RET_N(cancel_by_host_inactive_server, cancel, TT_NO_LOGS, BY_HOST | INACTIVE_SERVER), + HTTP_RET_N(cancel_by_host_no_ns, cancel, TT_NO_LOGS, BY_HOST | NO_NS), + HTTP_N(cancel_inactive_server, cancel, 0, INACTIVE_SERVER), + HTTP_N(cancel_by_host_no_ns_inactive_server, cancel, TT_NO_LOGS, BY_HOST | NO_NS | INACTIVE_SERVER), HTTP_OFF_N(cancel_by_host_server_timeout, cancel, BY_HOST | INACTIVE_SERVER | SERVER_TIMEOUT), HTTP_OFF_N(cancel_server_timeout, cancel, INACTIVE_SERVER | SERVER_TIMEOUT), HTTP_OFF_N(cancel_by_host_no_ns_server_timeout, cancel, BY_HOST | NO_NS | INACTIVE_SERVER | SERVER_TIMEOUT), HTTP_OFF_N(cancel_by_host_ns_timeout_server_timeout, cancel, BY_HOST | NO_NS | NS_TIMEOUT | INACTIVE_SERVER | SERVER_TIMEOUT), - HTTP_N(cancel_by_host_ns_timeout, cancel, BY_HOST | NO_NS | NS_TIMEOUT), - HTTP_N(cancel_by_host_ns_timeout_inactive_server, cancel, BY_HOST | NO_NS | NS_TIMEOUT | INACTIVE_SERVER), + HTTP_RET_N(cancel_by_host_ns_timeout, cancel, TT_NO_LOGS, BY_HOST | NO_NS | NS_TIMEOUT), + HTTP_RET_N(cancel_by_host_ns_timeout_inactive_server, cancel, TT_NO_LOGS, BY_HOST | NO_NS | NS_TIMEOUT | INACTIVE_SERVER), HTTP(virtual_host), HTTP(post), @@ -4630,6 +4774,8 @@ struct testcase_t http_testcases[] = { HTTP(write_during_read), HTTP(request_own), + HTTP(request_extra_body), + #ifdef EVENT__HAVE_OPENSSL HTTPS(basic), HTTPS(filter_basic), @@ -4652,3 +4798,10 @@ struct testcase_t http_testcases[] = { END_OF_TESTCASES }; +struct testcase_t http_iocp_testcases[] = { + { "simple", http_simple_test, TT_FORK|TT_NEED_BASE|TT_ENABLE_IOCP, &basic_setup, NULL }, +#ifdef EVENT__HAVE_OPENSSL + { "https_simple", https_simple_test, TT_FORK|TT_NEED_BASE|TT_ENABLE_IOCP, &basic_setup, NULL }, +#endif + END_OF_TESTCASES +}; diff --git a/test/regress_listener.c b/test/regress_listener.c index 72f7237be6e4..871da4c62ce9 100644 --- a/test/regress_listener.c +++ b/test/regress_listener.c @@ -80,8 +80,9 @@ regress_pick_a_port(void *arg) ev_socklen_t slen1 = sizeof(ss1), slen2 = sizeof(ss2); unsigned int flags = LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_EXEC; + evutil_socket_t fd1, fd2, fd3; - evutil_socket_t fd1 = -1, fd2 = -1, fd3 = -1; + fd1 = fd2 = fd3 = EVUTIL_INVALID_SOCKET; if (data->setup_data && strstr((char*)data->setup_data, "ts")) { flags |= LEV_OPT_THREADSAFE; @@ -99,8 +100,8 @@ regress_pick_a_port(void *arg) flags, -1, (struct sockaddr *)&sin, sizeof(sin)); tt_assert(listener2); - tt_int_op(evconnlistener_get_fd(listener1), >=, 0); - tt_int_op(evconnlistener_get_fd(listener2), >=, 0); + tt_assert(evconnlistener_get_fd(listener1) != EVUTIL_INVALID_SOCKET); + tt_assert(evconnlistener_get_fd(listener2) != EVUTIL_INVALID_SOCKET); tt_assert(getsockname(evconnlistener_get_fd(listener1), (struct sockaddr*)&ss1, &slen1) == 0); tt_assert(getsockname(evconnlistener_get_fd(listener2), @@ -117,7 +118,7 @@ regress_pick_a_port(void *arg) tt_ptr_op(evconnlistener_get_base(listener1), ==, base); tt_ptr_op(evconnlistener_get_base(listener2), ==, base); - fd1 = fd2 = fd3 = -1; + fd1 = fd2 = fd3 = EVUTIL_INVALID_SOCKET; evutil_socket_connect_(&fd1, (struct sockaddr*)&ss1, slen1); evutil_socket_connect_(&fd2, (struct sockaddr*)&ss1, slen1); evutil_socket_connect_(&fd3, (struct sockaddr*)&ss2, slen2); @@ -185,6 +186,89 @@ end: evconnlistener_free(listener); } +static void +acceptcb_free(struct evconnlistener *listener, evutil_socket_t fd, + struct sockaddr *addr, int socklen, void *arg) +{ + int *ptr = arg; + --*ptr; + TT_BLATHER(("Got one for %p", ptr)); + evutil_closesocket(fd); + + if (! *ptr) + evconnlistener_free(listener); +} +static void +regress_listener_close_accepted_fd(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct evconnlistener *listener = NULL; + struct sockaddr_in sin; + struct sockaddr_storage ss; + ev_socklen_t slen = sizeof(ss); + int count = 1; + unsigned int flags = LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE; + evutil_socket_t fd = EVUTIL_INVALID_SOCKET; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */ + sin.sin_port = 0; /* "You pick!" */ + + /* Start a listener with a bogus socket. */ + listener = evconnlistener_new_bind(base, acceptcb_free, &count, + flags, -1, (struct sockaddr *)&sin, sizeof(sin)); + tt_assert(listener); + + tt_assert(getsockname(evconnlistener_get_fd(listener), + (struct sockaddr*)&ss, &slen) == 0); + evutil_socket_connect_(&fd, (struct sockaddr*)&ss, slen); + + event_base_dispatch(base); + +end: + ; +} + +static void +regress_listener_immediate_close(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct evconnlistener *listener = NULL; + struct sockaddr_in sin; + struct sockaddr_storage ss; + ev_socklen_t slen = sizeof(ss); + int count = 1; + unsigned int flags = LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE; + evutil_socket_t fd1 = EVUTIL_INVALID_SOCKET, fd2 = EVUTIL_INVALID_SOCKET; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */ + sin.sin_port = 0; /* "You pick!" */ + + /* Start a listener with a bogus socket. */ + listener = evconnlistener_new_bind(base, acceptcb, &count, + flags, -1, (struct sockaddr *)&sin, sizeof(sin)); + tt_assert(listener); + + tt_assert(getsockname(evconnlistener_get_fd(listener), + (struct sockaddr*)&ss, &slen) == 0); + + evutil_socket_connect_(&fd1, (struct sockaddr*)&ss, slen); + evutil_socket_connect_(&fd2, (struct sockaddr*)&ss, slen); + + event_base_dispatch(base); + + tt_int_op(count, ==, 0); + +end: + if (listener) + evconnlistener_free(listener); +} + #ifdef EVENT__HAVE_SETRLIMIT static void regress_listener_error_unlock(void *arg) @@ -230,7 +314,7 @@ struct testcase_t listener_testcases[] = { #ifdef EVENT__HAVE_SETRLIMIT { "error_unlock", regress_listener_error_unlock, - TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR, + TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR|TT_NO_LOGS, &basic_setup, NULL}, #endif @@ -242,6 +326,12 @@ struct testcase_t listener_testcases[] = { TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR, &basic_setup, (char*)"ts"}, + { "close_accepted_fd", regress_listener_close_accepted_fd, + TT_FORK|TT_NEED_BASE, &basic_setup, NULL, }, + + { "immediate_close", regress_listener_immediate_close, + TT_FORK|TT_NEED_BASE, &basic_setup, NULL, }, + END_OF_TESTCASES, }; diff --git a/test/regress_main.c b/test/regress_main.c index 44e291119a3d..266561214b8e 100644 --- a/test/regress_main.c +++ b/test/regress_main.c @@ -33,6 +33,14 @@ #include <fcntl.h> #endif +/* move_pthread_to_realtime_scheduling_class() */ +#ifdef EVENT__HAVE_MACH_MACH_H +#include <mach/mach.h> +#endif +#ifdef EVENT__HAVE_MACH_MACH_TIME_H +#include <mach/mach_time.h> +#endif + #if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) #if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 && \ __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070) @@ -81,10 +89,12 @@ #include "event2/event-config.h" #include "regress.h" +#include "regress_thread.h" #include "tinytest.h" #include "tinytest_macros.h" #include "../iocp-internal.h" #include "../event-internal.h" +#include "../evthread-internal.h" struct evutil_weakrand_state test_weakrand_state; @@ -186,18 +196,65 @@ ignore_log_cb(int s, const char *msg) { } -static void * +/** + * Put into the real time scheduling class for better timers latency. + * https://developer.apple.com/library/archive/technotes/tn2169/_index.html#//apple_ref/doc/uid/DTS40013172-CH1-TNTAG6000 + */ +#if defined(__APPLE__) +static void move_pthread_to_realtime_scheduling_class(pthread_t pthread) +{ + mach_timebase_info_data_t info; + mach_timebase_info(&info); + + const uint64_t NANOS_PER_MSEC = 1000000ULL; + double clock2abs = + ((double)info.denom / (double)info.numer) * NANOS_PER_MSEC; + + thread_time_constraint_policy_data_t policy; + policy.period = 0; + policy.computation = (uint32_t)(5 * clock2abs); // 5 ms of work + policy.constraint = (uint32_t)(10 * clock2abs); + policy.preemptible = FALSE; + + int kr = thread_policy_set(pthread_mach_thread_np(pthread), + THREAD_TIME_CONSTRAINT_POLICY, + (thread_policy_t)&policy, + THREAD_TIME_CONSTRAINT_POLICY_COUNT); + if (kr != KERN_SUCCESS) { + mach_error("thread_policy_set:", kr); + exit(1); + } +} + +void thread_setup(THREAD_T pthread) +{ + move_pthread_to_realtime_scheduling_class(pthread); +} +#else /** \__APPLE__ */ +void thread_setup(THREAD_T pthread) {} +#endif /** \!__APPLE__ */ + + +void * basic_test_setup(const struct testcase_t *testcase) { struct event_base *base = NULL; evutil_socket_t spair[2] = { -1, -1 }; struct basic_test_data *data = NULL; + thread_setup(THREAD_SELF()); + #ifndef _WIN32 if (testcase->flags & TT_ENABLE_IOCP_FLAG) return (void*)TT_SKIP; #endif + if (testcase->flags & TT_ENABLE_DEBUG_MODE && + !libevent_tests_running_in_debug_mode) { + event_enable_debug_mode(); + libevent_tests_running_in_debug_mode = 1; + } + if (testcase->flags & TT_NEED_THREADS) { if (!(testcase->flags & TT_FORK)) return NULL; @@ -262,7 +319,7 @@ basic_test_setup(const struct testcase_t *testcase) return data; } -static int +int basic_test_cleanup(const struct testcase_t *testcase, void *ptr) { struct basic_test_data *data = ptr; @@ -384,6 +441,7 @@ struct testgroup_t testgroups[] = { { "iocp/", iocp_testcases }, { "iocp/bufferevent/", bufferevent_iocp_testcases }, { "iocp/listener/", listener_iocp_testcases }, + { "iocp/http/", http_iocp_testcases }, #endif #ifdef EVENT__HAVE_OPENSSL { "ssl/", ssl_testcases }, @@ -435,6 +493,7 @@ main(int argc, const char **argv) #ifdef _WIN32 tinytest_skip(testgroups, "http/connection_retry"); tinytest_skip(testgroups, "http/https_connection_retry"); + tinytest_skip(testgroups, "http/read_on_write_error"); #endif #ifndef EVENT__DISABLE_THREAD_SUPPORT @@ -454,6 +513,11 @@ main(int argc, const char **argv) evutil_weakrand_seed_(&test_weakrand_state, 0); + if (getenv("EVENT_NO_FILE_BUFFERING")) { + setbuf(stdout, NULL); + setbuf(stderr, NULL); + } + if (tinytest_main(argc,argv,testgroups)) return 1; diff --git a/test/regress_rpc.c b/test/regress_rpc.c index 01a058cbb2b8..87a7efa12fc3 100644 --- a/test/regress_rpc.c +++ b/test/regress_rpc.c @@ -61,7 +61,6 @@ #include "event2/http_compat.h" #include "event2/http_struct.h" #include "event2/rpc.h" -#include "event2/rpc.h" #include "event2/rpc_struct.h" #include "event2/tag.h" #include "log-internal.h" @@ -880,6 +879,53 @@ end: evbuffer_free(tmp); } +static void +rpc_invalid_type(void) +{ + ev_uint16_t port; + struct evhttp *http = NULL; + struct evrpc_base *base = NULL; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + + rpc_setup(&http, &port, &base); + + evcon = evhttp_connection_new("127.0.0.1", port); + tt_assert(evcon); + + /* + * At this point, we want to schedule an HTTP POST request + * server using our make request method. + */ + + req = evhttp_request_new(rpc_postrequest_failure, NULL); + tt_assert(req); + + /* Add the information that we care about */ + evhttp_add_header(req->output_headers, "Host", "somehost"); + evbuffer_add_printf(req->output_buffer, "Some Nonsense"); + + if (evhttp_make_request(evcon, req, + EVHTTP_REQ_GET, + "/.rpc.Message") == -1) { + tt_abort(); + } + + test_ok = 0; + + event_dispatch(); + + evhttp_connection_free(evcon); + + rpc_teardown(base); + + tt_assert(test_ok == 1); + +end: + evhttp_free(http); +} + + #define RPC_LEGACY(name) \ { #name, run_legacy_test_fn, TT_FORK|TT_NEED_BASE|TT_LEGACY, \ &legacy_setup, \ @@ -898,6 +944,7 @@ struct testcase_t rpc_testcases[] = { RPC_LEGACY(basic_client), RPC_LEGACY(basic_queued_client), RPC_LEGACY(basic_client_with_pause), + RPC_LEGACY(invalid_type), RPC_LEGACY(client_timeout), RPC_LEGACY(test), diff --git a/test/regress_ssl.c b/test/regress_ssl.c index 681705fcf969..37dc334dcafa 100644 --- a/test/regress_ssl.c +++ b/test/regress_ssl.c @@ -53,7 +53,6 @@ #include "tinytest.h" #include "tinytest_macros.h" -#include <openssl/bio.h> #include <openssl/err.h> #include <openssl/pem.h> #include "openssl-compat.h" @@ -119,14 +118,13 @@ end: } X509 * -ssl_getcert(void) +ssl_getcert(EVP_PKEY *key) { /* Dummy code to make a quick-and-dirty valid certificate with OpenSSL. Don't copy this code into your own program! It does a number of things in a stupid and insecure way. */ X509 *x509 = NULL; X509_NAME *name = NULL; - EVP_PKEY *key = ssl_getkey(); int nid; time_t now = time(NULL); @@ -148,16 +146,18 @@ ssl_getcert(void) X509_set_subject_name(x509, name); X509_set_issuer_name(x509, name); + X509_NAME_free(name); - X509_time_adj(X509_get_notBefore(x509), 0, &now); + X509_time_adj(X509_getm_notBefore(x509), 0, &now); now += 3600; - X509_time_adj(X509_get_notAfter(x509), 0, &now); + X509_time_adj(X509_getm_notAfter(x509), 0, &now); X509_set_pubkey(x509, key); tt_assert(0 != X509_sign(x509, key, EVP_sha1())); return x509; end: X509_free(x509); + X509_NAME_free(name); return NULL; } @@ -183,35 +183,86 @@ get_ssl_ctx(void) return the_ssl_ctx; } +static int test_is_done; +static int n_connected; +static int got_close; +static int got_error; +static int got_timeout; +static int renegotiate_at = -1; +static int stop_when_connected; +static int pending_connect_events; +static struct event_base *exit_base; +static X509 *the_cert; +EVP_PKEY *the_key; + void init_ssl(void) { -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L) SSL_library_init(); ERR_load_crypto_strings(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); if (SSLeay() != OPENSSL_VERSION_NUMBER) { - TT_DECLARE("WARN", ("Version mismatch for openssl: compiled with %lx but running with %lx", (unsigned long)OPENSSL_VERSION_NUMBER, (unsigned long) SSLeay())); + TT_DECLARE("WARN", + ("Version mismatch for openssl: compiled with %lx but running with %lx", + (unsigned long)OPENSSL_VERSION_NUMBER, (unsigned long)SSLeay())); } #endif } +static void * +ssl_test_setup(const struct testcase_t *testcase) +{ + init_ssl(); + + the_key = ssl_getkey(); + EVUTIL_ASSERT(the_key); + + the_cert = ssl_getcert(the_key); + EVUTIL_ASSERT(the_cert); + + disable_tls_11_and_12 = 0; + + return basic_test_setup(testcase); +} +static int +ssl_test_cleanup(const struct testcase_t *testcase, void *ptr) +{ + int ret = basic_test_cleanup(testcase, ptr); + if (!ret) { + return ret; + } + + test_is_done = 0; + n_connected = 0; + got_close = 0; + got_error = 0; + got_timeout = 0; + renegotiate_at = -1; + stop_when_connected = 0; + pending_connect_events = 0; + exit_base = NULL; + + X509_free(the_cert); + EVP_PKEY_free(the_key); + + SSL_CTX_free(the_ssl_ctx); + the_ssl_ctx = NULL; + + return 1; +} +const struct testcase_setup_t ssl_setup = { + ssl_test_setup, ssl_test_cleanup +}; + + /* ==================== Here's a simple test: we read a number from the input, increment it, and reply, until we get to 1001. */ -static int test_is_done = 0; -static int n_connected = 0; -static int got_close = 0; -static int got_error = 0; -static int got_timeout = 0; -static int renegotiate_at = -1; -static int stop_when_connected = 0; -static int pending_connect_events = 0; -static struct event_base *exit_base = NULL; - enum regress_openssl_type { REGRESS_OPENSSL_SOCKETPAIR = 1, @@ -229,17 +280,19 @@ enum regress_openssl_type REGRESS_OPENSSL_SLEEP = 1024, REGRESS_OPENSSL_CLIENT_WRITE = 2048, + + REGRESS_DEFERRED_CALLBACKS = 4096, }; static void bufferevent_openssl_check_fd(struct bufferevent *bev, int filter) { - tt_int_op(bufferevent_getfd(bev), !=, -1); - tt_int_op(bufferevent_setfd(bev, -1), ==, 0); + tt_fd_op(bufferevent_getfd(bev), !=, EVUTIL_INVALID_SOCKET); + tt_fd_op(bufferevent_setfd(bev, EVUTIL_INVALID_SOCKET), ==, 0); if (filter) { - tt_int_op(bufferevent_getfd(bev), !=, -1); + tt_fd_op(bufferevent_getfd(bev), !=, EVUTIL_INVALID_SOCKET); } else { - tt_int_op(bufferevent_getfd(bev), ==, -1); + tt_fd_op(bufferevent_getfd(bev), ==, EVUTIL_INVALID_SOCKET); } end: @@ -256,6 +309,13 @@ end: } static void +free_on_cb(struct bufferevent *bev, void *ctx) +{ + TT_BLATHER(("free_on_cb: %p", bev)); + bufferevent_free(bev); +} + +static void respond_to_number(struct bufferevent *bev, void *ctx) { struct evbuffer *b = bufferevent_get_input(bev); @@ -303,13 +363,14 @@ done_writing_cb(struct bufferevent *bev, void *ctx) static void eventcb(struct bufferevent *bev, short what, void *ctx) { + X509 *peer_cert = NULL; enum regress_openssl_type type; + type = (enum regress_openssl_type)ctx; TT_BLATHER(("Got event %d", (int)what)); if (what & BEV_EVENT_CONNECTED) { SSL *ssl; - X509 *peer_cert; ++n_connected; ssl = bufferevent_openssl_get_ssl(bev); tt_assert(ssl); @@ -357,8 +418,10 @@ eventcb(struct bufferevent *bev, short what, void *ctx) } bufferevent_free(bev); } + end: - ; + if (peer_cert) + X509_free(peer_cert); } static void @@ -398,8 +461,6 @@ regress_bufferevent_openssl(void *arg) struct bufferevent *bev1, *bev2; SSL *ssl1, *ssl2; - X509 *cert = ssl_getcert(); - EVP_PKEY *key = ssl_getkey(); int flags = BEV_OPT_DEFER_CALLBACKS; struct bufferevent *bev_ll[2] = { NULL, NULL }; evutil_socket_t *fd_pair = NULL; @@ -407,14 +468,9 @@ regress_bufferevent_openssl(void *arg) enum regress_openssl_type type; type = (enum regress_openssl_type)data->setup_data; - tt_assert(cert); - tt_assert(key); - - init_ssl(); - if (type & REGRESS_OPENSSL_RENEGOTIATE) { - if (SSLeay() >= 0x10001000 && - SSLeay() < 0x1000104f) { + if (OPENSSL_VERSION_NUMBER >= 0x10001000 && + OPENSSL_VERSION_NUMBER < 0x1000104f) { /* 1.0.1 up to 1.0.1c has a bug where TLS1.1 and 1.2 * can't renegotiate with themselves. Disable. */ disable_tls_11_and_12 = 1; @@ -425,8 +481,8 @@ regress_bufferevent_openssl(void *arg) ssl1 = SSL_new(get_ssl_ctx()); ssl2 = SSL_new(get_ssl_ctx()); - SSL_use_certificate(ssl2, cert); - SSL_use_PrivateKey(ssl2, key); + SSL_use_certificate(ssl2, the_cert); + SSL_use_PrivateKey(ssl2, the_key); if (!(type & REGRESS_OPENSSL_OPEN)) flags |= BEV_OPT_CLOSE_ON_FREE; @@ -445,7 +501,7 @@ regress_bufferevent_openssl(void *arg) fd_pair, bev_ll, type); if (!(type & REGRESS_OPENSSL_FILTER)) { - tt_int_op(bufferevent_getfd(bev1), ==, data->pair[0]); + tt_fd_op(bufferevent_getfd(bev1), ==, data->pair[0]); } else { tt_ptr_op(bufferevent_get_underlying(bev1), ==, bev_ll[0]); } @@ -504,7 +560,10 @@ regress_bufferevent_openssl(void *arg) tt_int_op(got_close, ==, 0); tt_int_op(got_error, ==, 0); tt_int_op(got_timeout, ==, 1); + + bufferevent_free(bev2); } + end: return; } @@ -526,15 +585,13 @@ acceptcb(struct evconnlistener *listener, evutil_socket_t fd, type = (enum regress_openssl_type)data->setup_data; - SSL_use_certificate(ssl, ssl_getcert()); - SSL_use_PrivateKey(ssl, ssl_getkey()); + SSL_use_certificate(ssl, the_cert); + SSL_use_PrivateKey(ssl, the_key); bev = bufferevent_openssl_socket_new( - data->base, - fd, - ssl, - BUFFEREVENT_SSL_ACCEPTING, + data->base, fd, ssl, BUFFEREVENT_SSL_ACCEPTING, BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); + tt_assert(bev); bufferevent_setcb(bev, respond_to_number, NULL, eventcb, (void*)(REGRESS_OPENSSL_SERVER)); @@ -550,11 +607,14 @@ acceptcb(struct evconnlistener *listener, evutil_socket_t fd, /* Only accept once, then disable ourself. */ evconnlistener_disable(listener); + +end: + ; } struct rwcount { - int fd; + evutil_socket_t fd; size_t read; size_t write; }; @@ -568,6 +628,7 @@ bio_rwcount_new(BIO *b) static int bio_rwcount_free(BIO *b) { + TT_BLATHER(("bio_rwcount_free: %p", b)); if (!b) return 0; if (BIO_get_shutdown(b)) { @@ -590,7 +651,6 @@ bio_rwcount_read(BIO *b, char *out, int outlen) static int bio_rwcount_write(BIO *b, const char *in, int inlen) { - struct rwcount *rw = BIO_get_data(b); ev_ssize_t ret = send(rw->fd, in, inlen, 0); ++rw->write; @@ -602,8 +662,12 @@ bio_rwcount_write(BIO *b, const char *in, int inlen) static long bio_rwcount_ctrl(BIO *b, int cmd, long num, void *ptr) { + struct rwcount *rw = BIO_get_data(b); long ret = 0; switch (cmd) { + case BIO_C_GET_FD: + ret = rw->fd; + break; case BIO_CTRL_GET_CLOSE: ret = BIO_get_shutdown(b); break; @@ -672,14 +736,11 @@ regress_bufferevent_openssl_connect(void *arg) struct sockaddr_storage ss; ev_socklen_t slen; SSL *ssl; - BIO *bio; struct rwcount rw = { -1, 0, 0 }; enum regress_openssl_type type; type = (enum regress_openssl_type)data->setup_data; - init_ssl(); - memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(0x7f000001); @@ -703,7 +764,7 @@ regress_bufferevent_openssl_connect(void *arg) BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); tt_assert(bev); - bufferevent_setcb(bev, respond_to_number, NULL, eventcb, + bufferevent_setcb(bev, respond_to_number, free_on_cb, eventcb, (void*)(REGRESS_OPENSSL_CLIENT)); tt_assert(getsockname(evconnlistener_get_fd(listener), @@ -716,6 +777,8 @@ regress_bufferevent_openssl_connect(void *arg) /* Possible only when we have fd, since be_openssl can and will overwrite * bio otherwise before */ if (type & REGRESS_OPENSSL_SLEEP) { + BIO *bio; + rw.fd = bufferevent_getfd(bev); bio = BIO_new_rwcount(0); tt_assert(bio); @@ -730,83 +793,284 @@ regress_bufferevent_openssl_connect(void *arg) tt_int_op(rw.read, <=, 100); tt_int_op(rw.write, <=, 100); end: - ; + evconnlistener_free(listener); +} + +struct wm_context +{ + int server; + int flags; + struct evbuffer *data; + size_t to_read; + size_t wm_high; + size_t limit; + size_t get; + struct bufferevent *bev; + struct wm_context *neighbour; +}; +static void +wm_transfer(struct bufferevent *bev, void *arg) +{ + struct wm_context *ctx = arg; + struct evbuffer *in = bufferevent_get_input(bev); + struct evbuffer *out = bufferevent_get_output(bev); + size_t len = evbuffer_get_length(in); + size_t drain = len < ctx->to_read ? len : ctx->to_read; + + if (ctx->get >= ctx->limit) { + TT_BLATHER(("wm_transfer-%s(%p): break", + ctx->server ? "server" : "client", bev)); + bufferevent_setcb(bev, NULL, NULL, NULL, NULL); + bufferevent_disable(bev, EV_READ); + if (ctx->neighbour->get >= ctx->neighbour->limit) { + event_base_loopbreak(bufferevent_get_base(bev)); + } + } else { + ctx->get += drain; + evbuffer_drain(in, drain); + } + + TT_BLATHER(("wm_transfer-%s(%p): " + "in: " EV_SIZE_FMT ", " + "out: " EV_SIZE_FMT ", " + "got: " EV_SIZE_FMT "", + ctx->server ? "server" : "client", bev, + evbuffer_get_length(in), + evbuffer_get_length(out), + ctx->get)); + + evbuffer_add_buffer_reference(out, ctx->data); +} +static void +wm_eventcb(struct bufferevent *bev, short what, void *arg) +{ + struct wm_context *ctx = arg; + TT_BLATHER(("wm_eventcb-%s(%p): %i", + ctx->server ? "server" : "client", bev, what)); + if (what & BEV_EVENT_CONNECTED) { + } else { + ctx->get = 0; + } +} +static void +wm_acceptcb(struct evconnlistener *listener, evutil_socket_t fd, + struct sockaddr *addr, int socklen, void *arg) +{ + struct wm_context *ctx = arg; + struct bufferevent *bev; + struct event_base *base = evconnlistener_get_base(listener); + SSL *ssl = SSL_new(get_ssl_ctx()); + + SSL_use_certificate(ssl, the_cert); + SSL_use_PrivateKey(ssl, the_key); + + bev = bufferevent_openssl_socket_new( + base, fd, ssl, BUFFEREVENT_SSL_ACCEPTING, ctx->flags); + + TT_BLATHER(("wm_transfer-%s(%p): accept", + ctx->server ? "server" : "client", bev)); + + bufferevent_setwatermark(bev, EV_READ, 0, ctx->wm_high); + bufferevent_setcb(bev, wm_transfer, NULL, wm_eventcb, ctx); + bufferevent_enable(bev, EV_READ|EV_WRITE); + ctx->bev = bev; + + /* Only accept once, then disable ourself. */ + evconnlistener_disable(listener); +} +static void +regress_bufferevent_openssl_wm(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + + struct evconnlistener *listener; + struct bufferevent *bev; + struct sockaddr_in sin; + struct sockaddr_storage ss; + enum regress_openssl_type type = + (enum regress_openssl_type)data->setup_data; + int bev_flags = BEV_OPT_CLOSE_ON_FREE; + ev_socklen_t slen; + SSL *ssl; + struct wm_context client, server; + char *payload; + size_t payload_len = 1<<10; + size_t wm_high = 5<<10; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(0x7f000001); + + memset(&ss, 0, sizeof(ss)); + slen = sizeof(ss); + + if (type & REGRESS_DEFERRED_CALLBACKS) + bev_flags |= BEV_OPT_DEFER_CALLBACKS; + + memset(&client, 0, sizeof(client)); + memset(&server, 0, sizeof(server)); + client.server = 0; + server.server = 1; + client.flags = server.flags = bev_flags; + client.data = evbuffer_new(); + server.data = evbuffer_new(); + payload = calloc(1, payload_len); + memset(payload, 'A', payload_len); + evbuffer_add(server.data, payload, payload_len); + evbuffer_add(client.data, payload, payload_len); + client.wm_high = server.wm_high = wm_high; + client.limit = server.limit = wm_high<<3; + client.to_read = server.to_read = payload_len>>1; + + TT_BLATHER(("openssl_wm: " + "payload_len = " EV_SIZE_FMT ", " + "wm_high = " EV_SIZE_FMT ", " + "limit = " EV_SIZE_FMT ", " + "to_read: " EV_SIZE_FMT "", + payload_len, + wm_high, + server.limit, + server.to_read)); + + listener = evconnlistener_new_bind(base, wm_acceptcb, &server, + LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, + -1, (struct sockaddr *)&sin, sizeof(sin)); + + tt_assert(listener); + tt_assert(evconnlistener_get_fd(listener) >= 0); + + ssl = SSL_new(get_ssl_ctx()); + tt_assert(ssl); + + if (type & REGRESS_OPENSSL_FILTER) { + bev = bufferevent_socket_new(data->base, -1, client.flags); + tt_assert(bev); + bev = bufferevent_openssl_filter_new( + base, bev, ssl, BUFFEREVENT_SSL_CONNECTING, client.flags); + } else { + bev = bufferevent_openssl_socket_new( + data->base, -1, ssl, + BUFFEREVENT_SSL_CONNECTING, + client.flags); + } + tt_assert(bev); + client.bev = bev; + + server.neighbour = &client; + client.neighbour = &server; + + bufferevent_setwatermark(bev, EV_READ, 0, client.wm_high); + bufferevent_setcb(bev, wm_transfer, NULL, wm_eventcb, &client); + + tt_assert(getsockname(evconnlistener_get_fd(listener), + (struct sockaddr*)&ss, &slen) == 0); + + tt_assert(!bufferevent_socket_connect(bev, (struct sockaddr*)&ss, slen)); + tt_assert(!evbuffer_add_buffer_reference(bufferevent_get_output(bev), client.data)); + tt_assert(!bufferevent_enable(bev, EV_READ|EV_WRITE)); + + event_base_dispatch(base); + + tt_int_op(client.get, ==, client.limit); + tt_int_op(server.get, ==, server.limit); + +end: + free(payload); + evbuffer_free(client.data); + evbuffer_free(server.data); + evconnlistener_free(listener); + bufferevent_free(client.bev); + bufferevent_free(server.bev); + + /* XXX: by some reason otherise there is a leak */ + if (!(type & REGRESS_OPENSSL_FILTER)) + event_base_loop(base, EVLOOP_ONCE); } struct testcase_t ssl_testcases[] = { #define T(a) ((void *)(a)) { "bufferevent_socketpair", regress_bufferevent_openssl, - TT_ISOLATED, &basic_setup, T(REGRESS_OPENSSL_SOCKETPAIR) }, + TT_ISOLATED, &ssl_setup, T(REGRESS_OPENSSL_SOCKETPAIR) }, { "bufferevent_socketpair_write_after_connect", regress_bufferevent_openssl, - TT_ISOLATED, &basic_setup, + TT_ISOLATED, &ssl_setup, T(REGRESS_OPENSSL_SOCKETPAIR|REGRESS_OPENSSL_CLIENT_WRITE) }, { "bufferevent_filter", regress_bufferevent_openssl, - TT_ISOLATED, &basic_setup, T(REGRESS_OPENSSL_FILTER) }, + TT_ISOLATED, &ssl_setup, T(REGRESS_OPENSSL_FILTER) }, { "bufferevent_filter_write_after_connect", regress_bufferevent_openssl, - TT_ISOLATED, &basic_setup, + TT_ISOLATED, &ssl_setup, T(REGRESS_OPENSSL_FILTER|REGRESS_OPENSSL_CLIENT_WRITE) }, { "bufferevent_renegotiate_socketpair", regress_bufferevent_openssl, - TT_ISOLATED, &basic_setup, + TT_ISOLATED, &ssl_setup, T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_RENEGOTIATE) }, { "bufferevent_renegotiate_filter", regress_bufferevent_openssl, - TT_ISOLATED, &basic_setup, + TT_ISOLATED, &ssl_setup, T(REGRESS_OPENSSL_FILTER | REGRESS_OPENSSL_RENEGOTIATE) }, { "bufferevent_socketpair_startopen", regress_bufferevent_openssl, - TT_ISOLATED, &basic_setup, + TT_ISOLATED, &ssl_setup, T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_OPEN) }, { "bufferevent_filter_startopen", regress_bufferevent_openssl, - TT_ISOLATED, &basic_setup, + TT_ISOLATED, &ssl_setup, T(REGRESS_OPENSSL_FILTER | REGRESS_OPENSSL_OPEN) }, { "bufferevent_socketpair_dirty_shutdown", regress_bufferevent_openssl, - TT_ISOLATED, &basic_setup, + TT_ISOLATED, &ssl_setup, T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_DIRTY_SHUTDOWN) }, { "bufferevent_filter_dirty_shutdown", regress_bufferevent_openssl, - TT_ISOLATED, &basic_setup, + TT_ISOLATED, &ssl_setup, T(REGRESS_OPENSSL_FILTER | REGRESS_OPENSSL_DIRTY_SHUTDOWN) }, { "bufferevent_renegotiate_socketpair_dirty_shutdown", regress_bufferevent_openssl, TT_ISOLATED, - &basic_setup, + &ssl_setup, T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_RENEGOTIATE | REGRESS_OPENSSL_DIRTY_SHUTDOWN) }, { "bufferevent_renegotiate_filter_dirty_shutdown", regress_bufferevent_openssl, TT_ISOLATED, - &basic_setup, + &ssl_setup, T(REGRESS_OPENSSL_FILTER | REGRESS_OPENSSL_RENEGOTIATE | REGRESS_OPENSSL_DIRTY_SHUTDOWN) }, { "bufferevent_socketpair_startopen_dirty_shutdown", regress_bufferevent_openssl, - TT_ISOLATED, &basic_setup, + TT_ISOLATED, &ssl_setup, T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_OPEN | REGRESS_OPENSSL_DIRTY_SHUTDOWN) }, { "bufferevent_filter_startopen_dirty_shutdown", regress_bufferevent_openssl, - TT_ISOLATED, &basic_setup, + TT_ISOLATED, &ssl_setup, T(REGRESS_OPENSSL_FILTER | REGRESS_OPENSSL_OPEN | REGRESS_OPENSSL_DIRTY_SHUTDOWN) }, { "bufferevent_socketpair_fd", regress_bufferevent_openssl, - TT_ISOLATED, &basic_setup, + TT_ISOLATED, &ssl_setup, T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_FD) }, { "bufferevent_socketpair_freed", regress_bufferevent_openssl, - TT_ISOLATED, &basic_setup, + TT_ISOLATED, &ssl_setup, T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_FREED) }, { "bufferevent_socketpair_freed_fd", regress_bufferevent_openssl, - TT_ISOLATED, &basic_setup, + TT_ISOLATED, &ssl_setup, T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_FREED | REGRESS_OPENSSL_FD) }, { "bufferevent_filter_freed_fd", regress_bufferevent_openssl, - TT_ISOLATED, &basic_setup, + TT_ISOLATED, &ssl_setup, T(REGRESS_OPENSSL_FILTER | REGRESS_OPENSSL_FREED | REGRESS_OPENSSL_FD) }, { "bufferevent_socketpair_timeout", regress_bufferevent_openssl, - TT_ISOLATED, &basic_setup, + TT_ISOLATED, &ssl_setup, T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_TIMEOUT) }, { "bufferevent_socketpair_timeout_freed_fd", regress_bufferevent_openssl, - TT_ISOLATED, &basic_setup, + TT_ISOLATED, &ssl_setup, T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_TIMEOUT | REGRESS_OPENSSL_FREED | REGRESS_OPENSSL_FD) }, { "bufferevent_connect", regress_bufferevent_openssl_connect, - TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + TT_FORK|TT_NEED_BASE, &ssl_setup, NULL }, { "bufferevent_connect_sleep", regress_bufferevent_openssl_connect, - TT_FORK|TT_NEED_BASE, &basic_setup, T(REGRESS_OPENSSL_SLEEP) }, + TT_FORK|TT_NEED_BASE, &ssl_setup, T(REGRESS_OPENSSL_SLEEP) }, + + { "bufferevent_wm", regress_bufferevent_openssl_wm, + TT_FORK|TT_NEED_BASE, &ssl_setup, NULL }, + { "bufferevent_wm_filter", regress_bufferevent_openssl_wm, + TT_FORK|TT_NEED_BASE, &ssl_setup, T(REGRESS_OPENSSL_FILTER) }, + { "bufferevent_wm_defer", regress_bufferevent_openssl_wm, + TT_FORK|TT_NEED_BASE, &ssl_setup, T(REGRESS_DEFERRED_CALLBACKS) }, + { "bufferevent_wm_filter_defer", regress_bufferevent_openssl_wm, + TT_FORK|TT_NEED_BASE, &ssl_setup, T(REGRESS_OPENSSL_FILTER|REGRESS_DEFERRED_CALLBACKS) }, #undef T diff --git a/test/regress_testutils.c b/test/regress_testutils.c index 959347ea7160..b0ce7dbba2a6 100644 --- a/test/regress_testutils.c +++ b/test/regress_testutils.c @@ -110,7 +110,7 @@ regress_get_dnsserver(struct event_base *base, memset(&my_addr, 0, sizeof(my_addr)); my_addr.sin_family = AF_INET; my_addr.sin_port = htons(*portnum); - my_addr.sin_addr.s_addr = htonl(0x7f000001UL); + my_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); if (bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr)) < 0) { evutil_closesocket(sock); tt_abort_perror("bind"); diff --git a/test/regress_thread.c b/test/regress_thread.c index 689c23d1fb80..1e0ce41ff95e 100644 --- a/test/regress_thread.c +++ b/test/regress_thread.c @@ -564,8 +564,8 @@ end: ; } -#define TEST(name) \ - { #name, thread_##name, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE, \ +#define TEST(name, f) \ + { #name, thread_##name, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE|(f), \ &basic_setup, NULL } struct testcase_t thread_testcases[] = { @@ -575,7 +575,7 @@ struct testcase_t thread_testcases[] = { { "forking", thread_basic, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE, &basic_setup, (char*)"forking" }, #endif - TEST(conditions_simple), + TEST(conditions_simple, TT_RETRIABLE), { "deferred_cb_skew", thread_deferred_cb_skew, TT_FORK|TT_NEED_THREADS|TT_OFF_BY_DEFAULT, &basic_setup, NULL }, @@ -583,7 +583,7 @@ struct testcase_t thread_testcases[] = { /****** XXX TODO FIXME windows seems to be having some timing trouble, * looking into it now. / ellzey ******/ - TEST(no_events), + TEST(no_events, TT_RETRIABLE), #endif END_OF_TESTCASES }; diff --git a/test/regress_thread.h b/test/regress_thread.h index 831b51e50739..a954fefa5611 100644 --- a/test/regress_thread.h +++ b/test/regress_thread.h @@ -27,22 +27,30 @@ #ifndef REGRESS_THREAD_H_INCLUDED_ #define REGRESS_THREAD_H_INCLUDED_ -#ifdef EVENT__HAVE_PTHREADS +#if defined(_WIN32) /** _WIN32 */ +#define THREAD_T void * /* HANDLE */ +#define THREAD_FN unsigned __stdcall +#define THREAD_RETURN() return (0) +#define THREAD_SELF() GetCurrentThreadId() +#define THREAD_START(threadvar, fn, arg) do { \ + uintptr_t threadhandle = _beginthreadex(NULL,0,fn,(arg),0,NULL); \ + (threadvar) = (THREAD_T)threadhandle; \ + thread_setup(threadvar); \ +} while (0) +#define THREAD_JOIN(th) WaitForSingleObject(th, INFINITE) +#else /* !_WIN32 */ +#include <pthread.h> #define THREAD_T pthread_t #define THREAD_FN void * #define THREAD_RETURN() return (NULL) -#define THREAD_START(threadvar, fn, arg) \ - pthread_create(&(threadvar), NULL, fn, arg) +#define THREAD_SELF() pthread_self() +#define THREAD_START(threadvar, fn, arg) do { \ + if (!pthread_create(&(threadvar), NULL, fn, arg)) \ + thread_setup(threadvar); \ +} while (0) #define THREAD_JOIN(th) pthread_join(th, NULL) -#else -#define THREAD_T HANDLE -#define THREAD_FN unsigned __stdcall -#define THREAD_RETURN() return (0) -#define THREAD_START(threadvar, fn, arg) do { \ - uintptr_t threadhandle = _beginthreadex(NULL,0,fn,(arg),0,NULL); \ - (threadvar) = (HANDLE) threadhandle; \ - } while (0) -#define THREAD_JOIN(th) WaitForSingleObject(th, INFINITE) -#endif +#endif /* \!_WIN32 */ + +void thread_setup(THREAD_T pthread); #endif diff --git a/test/regress_util.c b/test/regress_util.c index ef6a1487aae2..45caa2700a40 100644 --- a/test/regress_util.c +++ b/test/regress_util.c @@ -23,6 +23,10 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/** For event_debug() usage/coverage */ +#define EVENT_VISIBILITY_WANT_DLLIMPORT + #include "../util-internal.h" #ifdef _WIN32 @@ -207,6 +211,65 @@ regress_ipv6_parse(void *ptr) #endif } +static struct ipv6_entry_scope { + const char *addr; + ev_uint32_t res[4]; + unsigned scope; + enum entry_status status; +} ipv6_entries_scope[] = { + { "2001:DB8::", { 0x20010db8, 0, 0 }, 0, NORMAL }, + { "2001:DB8::%0", { 0x20010db8, 0, 0, 0 }, 0, NORMAL }, + { "2001:DB8::%1", { 0x20010db8, 0, 0, 0 }, 1, NORMAL }, + { "foobar.", { 0, 0, 0, 0 }, 0, BAD }, + { "2001:DB8::%does-not-exist", { 0, 0, 0, 0 }, 0, BAD }, + { NULL, { 0, 0, 0, 0, }, 0, BAD }, +}; +static void +regress_ipv6_parse_scope(void *ptr) +{ +#ifdef AF_INET6 + int i, j; + unsigned if_scope; + + for (i = 0; ipv6_entries_scope[i].addr; ++i) { + struct ipv6_entry_scope *ent = &ipv6_entries_scope[i]; + struct in6_addr in6; + int r; + r = evutil_inet_pton_scope(AF_INET6, ent->addr, &in6, + &if_scope); + if (r == 0) { + if (ent->status != BAD) + TT_FAIL(("%s did not parse, but it's a good address!", + ent->addr)); + continue; + } + if (ent->status == BAD) { + TT_FAIL(("%s parsed, but we expected an error", ent->addr)); + continue; + } + for (j = 0; j < 4; ++j) { + /* Can't use s6_addr32 here; some don't have it. */ + ev_uint32_t u = + ((ev_uint32_t)in6.s6_addr[j*4 ] << 24) | + ((ev_uint32_t)in6.s6_addr[j*4+1] << 16) | + ((ev_uint32_t)in6.s6_addr[j*4+2] << 8) | + ((ev_uint32_t)in6.s6_addr[j*4+3]); + if (u != ent->res[j]) { + TT_FAIL(("%s did not parse as expected.", ent->addr)); + continue; + } + } + if (if_scope != ent->scope) { + TT_FAIL(("%s did not parse as expected.", ent->addr)); + continue; + } + } +#else + TT_BLATHER(("Skipping IPv6 address parsing.")); +#endif +} + + static struct sa_port_ent { const char *parse; int safamily; @@ -922,6 +985,16 @@ end: } static void +test_EVUTIL_IS_(void *arg) +{ + tt_int_op(EVUTIL_ISDIGIT_('0'), ==, 1); + tt_int_op(EVUTIL_ISDIGIT_('a'), ==, 0); + tt_int_op(EVUTIL_ISDIGIT_('\xff'), ==, 0); +end: + ; +} + +static void test_evutil_getaddrinfo(void *arg) { struct evutil_addrinfo *ai = NULL, *a; @@ -1117,6 +1190,41 @@ end: evutil_freeaddrinfo(ai); } +static void +test_evutil_getaddrinfo_AI_ADDRCONFIG(void *arg) +{ + struct evutil_addrinfo *ai = NULL; + struct evutil_addrinfo hints; + int r; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = EVUTIL_AI_PASSIVE|EVUTIL_AI_ADDRCONFIG; + + /* IPv4 */ + r = evutil_getaddrinfo("127.0.0.1", "80", &hints, &ai); + tt_int_op(r, ==, 0); + tt_assert(ai); + tt_ptr_op(ai->ai_next, ==, NULL); + test_ai_eq(ai, "127.0.0.1:80", SOCK_STREAM, IPPROTO_TCP); + evutil_freeaddrinfo(ai); + ai = NULL; + + /* IPv6 */ + r = evutil_getaddrinfo("::1", "80", &hints, &ai); + tt_int_op(r, ==, 0); + tt_assert(ai); + tt_ptr_op(ai->ai_next, ==, NULL); + test_ai_eq(ai, "[::1]:80", SOCK_STREAM, IPPROTO_TCP); + evutil_freeaddrinfo(ai); + ai = NULL; + +end: + if (ai) + evutil_freeaddrinfo(ai); +} + #ifdef _WIN32 static void test_evutil_loadsyslib(void *arg) @@ -1412,10 +1520,12 @@ static struct date_rfc1123_case { { 1289433600, "Thu, 11 Nov 2010 00:00:00 GMT"}, { 1323648000, "Mon, 12 Dec 2011 00:00:00 GMT"}, #ifndef _WIN32 +#if EVENT__SIZEOF_TIME_T > 4 /** In win32 case we have max "23:59:59 January 18, 2038, UTC" for time32 */ { 4294967296, "Sun, 07 Feb 2106 06:28:16 GMT"} /* 2^32 */, /** In win32 case we have max "23:59:59, December 31, 3000, UTC" for time64 */ {253402300799, "Fri, 31 Dec 9999 23:59:59 GMT"} /* long long future no one can imagine */, +#endif /* time_t != 32bit */ { 1456704000, "Mon, 29 Feb 2016 00:00:00 GMT"} /* leap year */, #endif { 1435708800, "Wed, 01 Jul 2015 00:00:00 GMT"} /* leap second */, @@ -1453,9 +1563,87 @@ end: ; } +static void +test_evutil_v4addr_is_local(void *arg) +{ + struct sockaddr_in sin; + sin.sin_family = AF_INET; + + /* we use evutil_inet_pton() here to fill in network-byte order */ +#define LOCAL(str, yes) do { \ + tt_int_op(evutil_inet_pton(AF_INET, str, &sin.sin_addr), ==, 1); \ + tt_int_op(evutil_v4addr_is_local_(&sin.sin_addr), ==, yes); \ +} while (0) + + /** any */ + sin.sin_addr.s_addr = INADDR_ANY; + tt_int_op(evutil_v4addr_is_local_(&sin.sin_addr), ==, 1); + + /** loopback */ + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + tt_int_op(evutil_v4addr_is_local_(&sin.sin_addr), ==, 1); + LOCAL("127.0.0.1", 1); + LOCAL("127.255.255.255", 1); + LOCAL("121.0.0.1", 0); + + /** link-local */ + LOCAL("169.254.0.1", 1); + LOCAL("169.254.255.255", 1); + LOCAL("170.0.0.0", 0); + + /** Multicast */ + LOCAL("224.0.0.0", 1); + LOCAL("239.255.255.255", 1); + LOCAL("240.0.0.0", 0); +end: + ; +} + +static void +test_evutil_v6addr_is_local(void *arg) +{ + struct sockaddr_in6 sin6; + struct in6_addr anyaddr = IN6ADDR_ANY_INIT; + struct in6_addr loopback = IN6ADDR_LOOPBACK_INIT; + + sin6.sin6_family = AF_INET6; +#define LOCAL6(str, yes) do { \ + tt_int_op(evutil_inet_pton(AF_INET6, str, &sin6.sin6_addr), ==, 1);\ + tt_int_op(evutil_v6addr_is_local_(&sin6.sin6_addr), ==, yes); \ +} while (0) + + /** any */ + tt_int_op(evutil_v6addr_is_local_(&anyaddr), ==, 1); + LOCAL6("::0", 1); + + /** loopback */ + tt_int_op(evutil_v6addr_is_local_(&loopback), ==, 1); + LOCAL6("::1", 1); + + /** IPV4 mapped */ + LOCAL6("::ffff:0:0", 1); + /** IPv4 translated */ + LOCAL6("::ffff:0:0:0", 1); + /** IPv4/IPv6 translation */ + LOCAL6("64:ff9b::", 0); + /** Link-local */ + LOCAL6("fe80::", 1); + /** Multicast */ + LOCAL6("ff00::", 1); + /** Unspecified */ + LOCAL6("::", 1); + + /** Global Internet */ + LOCAL6("2001::", 0); + LOCAL6("2001:4860:4802:32::1b", 0); +end: + ; +} + struct testcase_t util_testcases[] = { { "ipv4_parse", regress_ipv4_parse, 0, NULL, NULL }, { "ipv6_parse", regress_ipv6_parse, 0, NULL, NULL }, + { "ipv6_parse_scope", regress_ipv6_parse_scope, 0, NULL, NULL }, { "sockaddr_port_parse", regress_sockaddr_port_parse, 0, NULL, NULL }, { "sockaddr_port_format", regress_sockaddr_port_format, 0, NULL, NULL }, { "sockaddr_predicates", test_evutil_sockaddr_predicates, 0,NULL,NULL }, @@ -1468,22 +1656,26 @@ struct testcase_t util_testcases[] = { { "upcast", test_evutil_upcast, 0, NULL, NULL }, { "integers", test_evutil_integers, 0, NULL, NULL }, { "rand", test_evutil_rand, TT_FORK, NULL, NULL }, + { "EVUTIL_IS_", test_EVUTIL_IS_, 0, NULL, NULL }, { "getaddrinfo", test_evutil_getaddrinfo, TT_FORK, NULL, NULL }, { "getaddrinfo_live", test_evutil_getaddrinfo_live, TT_FORK|TT_OFF_BY_DEFAULT, NULL, NULL }, + { "getaddrinfo_AI_ADDRCONFIG", test_evutil_getaddrinfo_AI_ADDRCONFIG, TT_FORK|TT_OFF_BY_DEFAULT, NULL, NULL }, #ifdef _WIN32 { "loadsyslib", test_evutil_loadsyslib, TT_FORK, NULL, NULL }, #endif { "mm_malloc", test_event_malloc, 0, NULL, NULL }, { "mm_calloc", test_event_calloc, 0, NULL, NULL }, { "mm_strdup", test_event_strdup, 0, NULL, NULL }, - { "usleep", test_evutil_usleep, 0, NULL, NULL }, + { "usleep", test_evutil_usleep, TT_RETRIABLE, NULL, NULL }, { "monotonic_res", test_evutil_monotonic_res, 0, &basic_setup, (void*)"" }, { "monotonic_res_precise", test_evutil_monotonic_res, TT_OFF_BY_DEFAULT, &basic_setup, (void*)"precise" }, { "monotonic_res_fallback", test_evutil_monotonic_res, TT_OFF_BY_DEFAULT, &basic_setup, (void*)"fallback" }, { "monotonic_prc", test_evutil_monotonic_prc, 0, &basic_setup, (void*)"" }, - { "monotonic_prc_precise", test_evutil_monotonic_prc, 0, &basic_setup, (void*)"precise" }, + { "monotonic_prc_precise", test_evutil_monotonic_prc, TT_RETRIABLE, &basic_setup, (void*)"precise" }, { "monotonic_prc_fallback", test_evutil_monotonic_prc, 0, &basic_setup, (void*)"fallback" }, { "date_rfc1123", test_evutil_date_rfc1123, 0, NULL, NULL }, + { "evutil_v4addr_is_local", test_evutil_v4addr_is_local, 0, NULL, NULL }, + { "evutil_v6addr_is_local", test_evutil_v6addr_is_local, 0, NULL, NULL }, END_OF_TESTCASES, }; diff --git a/test/rpcgen_wrapper.sh b/test/rpcgen_wrapper.sh index aaa03031a1f6..fe582d57bd33 100755 --- a/test/rpcgen_wrapper.sh +++ b/test/rpcgen_wrapper.sh @@ -25,19 +25,10 @@ exit_failed() { echo "Could not generate regress.gen.\[ch\] using event_rpcgen.sh" >&2 exit 1 } - -if [ -x /usr/bin/python2 ] ; then - PYTHON2=/usr/bin/python2 -elif [ "x`which python2`" != x ] ; then - PYTHON2=python2 -else - PYTHON2=python -fi - srcdir=$1 srcdir=${srcdir:-.} -${PYTHON2} ${srcdir}/../event_rpcgen.py --quiet ${srcdir}/regress.rpc \ +${srcdir}/../event_rpcgen.py --quiet ${srcdir}/regress.rpc \ test/regress.gen.h test/regress.gen.c case "$?" in diff --git a/test/test-changelist.c b/test/test-changelist.c index 6e2466d5a55c..fd1a17f5bd59 100644 --- a/test/test-changelist.c +++ b/test/test-changelist.c @@ -182,11 +182,11 @@ main(int argc, char **argv) if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) return (1); - /* Initalize the event library */ + /* Initialize the event library */ if (!(base = event_base_new())) return (1); - /* Initalize a timeout to terminate the test */ + /* Initialize a timeout to terminate the test */ timeout = evtimer_new(base,timeout_cb,&timeout); /* and watch for writability on one end of the pipe */ ev = event_new(base,pair[1],EV_WRITE | EV_PERSIST, write_cb, &ev); diff --git a/test/test-closed.c b/test/test-closed.c index 1dd988592d2a..9e6050408d64 100644 --- a/test/test-closed.c +++ b/test/test-closed.c @@ -104,6 +104,7 @@ main(int argc, char **argv) event_base_dispatch(base); /* Finalize library */ + event_free(ev); event_base_free(base); return 0; } diff --git a/test/test-eof.c b/test/test-eof.c index 284ead78ae34..de2fd88b991e 100644 --- a/test/test-eof.c +++ b/test/test-eof.c @@ -102,10 +102,10 @@ main(int argc, char **argv) return (1); shutdown(pair[0], EVUTIL_SHUT_WR); - /* Initalize the event library */ + /* Initialize the event library */ event_init(); - /* Initalize one event */ + /* Initialize one event */ event_set(&ev, pair[1], EV_READ | EV_TIMEOUT, read_cb, &ev); event_add(&ev, &timeout); diff --git a/test/test-fdleak.c b/test/test-fdleak.c index 4c4eba25e70e..a1fb4ed13bd2 100644 --- a/test/test-fdleak.c +++ b/test/test-fdleak.c @@ -95,8 +95,11 @@ server_event_cb(struct bufferevent *bev, short events, void *ctx) if (events & BEV_EVENT_ERROR) { my_perror("Error from bufferevent"); exit(1); - } else if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) { + } else if (events & BEV_EVENT_EOF) { bufferevent_free(bev); + if (num_requests == MAX_REQUESTS) { + event_base_loopbreak(bufferevent_get_base(bev)); + } } } @@ -107,8 +110,7 @@ listener_accept_cb(struct evconnlistener *listener, evutil_socket_t sock, { struct event_base *base = evconnlistener_get_base(listener); struct bufferevent *bev = bufferevent_socket_new(base, sock, - BEV_OPT_CLOSE_ON_FREE); - + BEV_OPT_CLOSE_ON_FREE); bufferevent_setcb(bev, server_read_cb, NULL, server_event_cb, NULL); bufferevent_enable(bev, EV_READ|EV_WRITE); } @@ -154,6 +156,9 @@ start_loop(void) start_client(base); event_base_dispatch(base); + + evconnlistener_free(listener); + event_base_free(base); } /* @@ -178,9 +183,7 @@ client_read_cb(struct bufferevent *bev, void *ctx) bufferevent_free(bev); num_requests++; - if (num_requests == MAX_REQUESTS) { - event_base_loopbreak(base); - } else { + if (++num_requests < MAX_REQUESTS) { start_client(base); } } diff --git a/test/test-init.c b/test/test-init.c index 92fbc6b14659..aea49ee94288 100644 --- a/test/test-init.c +++ b/test/test-init.c @@ -57,7 +57,7 @@ main(int argc, char **argv) (void) WSAStartup(wVersionRequested, &wsaData); #endif - /* Initalize the event library */ + /* Initialize the event library */ event_init(); return (0); diff --git a/test/test-ratelim.c b/test/test-ratelim.c index 9ee989bd820e..34112e39e14d 100644 --- a/test/test-ratelim.c +++ b/test/test-ratelim.c @@ -50,6 +50,10 @@ #include "event2/listener.h" #include "event2/thread.h" +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + static struct evutil_weakrand_state weakrand_state; static int cfg_verbose = 0; @@ -85,6 +89,18 @@ struct client_state { }; static const struct timeval *ms100_common=NULL; +/* Timers bias for slow CPUs, affects: + * - cfg_connlimit_tolerance (--check-connlimit) + * - cfg_grouplimit_tolerance (--check-grouplimit) + * - cfg_stddev_tolerance (--check-stddev) + */ +static int timer_bias_events; +static struct timeval timer_bias_start; +double timer_bias_spend; +/* Real cost is less (approximately ~5 usec), + * this macros adjusted to make the bias less */ +#define TIMER_MAX_COST_USEC 10 + /* info from check_bucket_levels_cb */ static int total_n_bev_checks = 0; static ev_int64_t total_rbucket_level=0; @@ -244,6 +260,64 @@ group_drain_cb(evutil_socket_t fd, short events, void *arg) bufferevent_rate_limit_group_decrement_write(ratelim_group, cfg_group_drain); } +static void +timer_bias_cb(evutil_socket_t fd, short events, void *arg) +{ + struct event *event = arg; + struct timeval end; + struct timeval diff; + + /** XXX: use rdtsc? (portability issues?) */ + evutil_gettimeofday(&end, NULL); + evutil_timersub(&end, &timer_bias_start, &diff); + timer_bias_spend += diff.tv_sec + diff.tv_usec * 1e6; + timer_bias_start = end; + + if (++timer_bias_events == 100) + event_del(event); +} +static double +timer_bias_calculate(void) +{ + struct event_config *cfg = NULL; + struct event_base *base = NULL; + struct event *timer = NULL; + struct timeval tv = { 0, 1 }; + int done = 0; + + cfg = event_config_new(); + if (!cfg) + goto err; + if (event_config_set_flag(cfg, EVENT_BASE_FLAG_PRECISE_TIMER)) + goto err; + base = event_base_new_with_config(cfg); + if (!base) + goto err; + + timer = event_new(base, -1, EV_PERSIST, timer_bias_cb, event_self_cbarg()); + if (!timer || event_add(timer, &tv)) { + goto err; + } + + evutil_gettimeofday(&timer_bias_start, NULL); + event_base_dispatch(base); + done = 1; + +err: + if (cfg) + event_config_free(cfg); + if (timer) + event_free(timer); + if (base) + event_base_free(base); + + if (done) + return MIN(timer_bias_spend / 1e6 / timer_bias_events / TIMER_MAX_COST_USEC, 5); + + fprintf(stderr, "Couldn't create event for CPU cycle counter bias\n"); + return -1; +} + static int test_ratelimiting(void) { @@ -266,6 +340,7 @@ test_ratelimiting(void) struct event_config *base_cfg; struct event *periodic_level_check; struct event *group_drain_event=NULL; + double timer_bias; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; @@ -275,6 +350,16 @@ test_ratelimiting(void) if (0) event_enable_debug_mode(); + timer_bias = timer_bias_calculate(); + if (timer_bias > 1) { + fprintf(stderr, "CPU is slow, timers bias is %f\n", timer_bias); + cfg_connlimit_tolerance *= timer_bias; + cfg_grouplimit_tolerance *= timer_bias; + cfg_stddev_tolerance *= timer_bias; + } else { + printf("CPU is fast enough, timers bias is %f\n", timer_bias); + } + base_cfg = event_config_new(); #ifdef _WIN32 @@ -376,7 +461,7 @@ test_ratelimiting(void) ratelim_group = NULL; /* So no more responders get added */ event_free(periodic_level_check); if (group_drain_event) - event_del(group_drain_event); + event_free(group_drain_event); for (i = 0; i < cfg_n_connections; ++i) { bufferevent_free(bevs[i]); diff --git a/test/test-time.c b/test/test-time.c index c4d031e72d62..a8b384626385 100644 --- a/test/test-time.c +++ b/test/test-time.c @@ -81,8 +81,10 @@ time_cb(evutil_socket_t fd, short event, void *arg) int main(int argc, char **argv) { + struct event_base *base; struct timeval tv; int i; + #ifdef _WIN32 WORD wVersionRequested; WSADATA wsaData; @@ -94,23 +96,28 @@ main(int argc, char **argv) evutil_weakrand_seed_(&weakrand_state, 0); - /* Initalize the event library */ - event_init(); + if (getenv("EVENT_DEBUG_LOGGING_ALL")) { + event_enable_debug_logging(EVENT_DBG_ALL); + } - for (i = 0; i < NEVENT; i++) { - ev[i] = malloc(sizeof(struct event)); + base = event_base_new(); - /* Initalize one event */ - evtimer_set(ev[i], time_cb, ev[i]); + for (i = 0; i < NEVENT; i++) { + ev[i] = evtimer_new(base, time_cb, event_self_cbarg()); tv.tv_sec = 0; tv.tv_usec = rand_int(50000); evtimer_add(ev[i], &tv); } - event_dispatch(); + i = event_base_dispatch(base); + printf("event_base_dispatch=%d, called=%d, EVENT=%d\n", + i, called, NEVENT); - printf("%d, %d\n", called, NEVENT); - return (called < NEVENT); + if (i == 1 && called >= NEVENT) { + return EXIT_SUCCESS; + } else { + return EXIT_FAILURE; + } } diff --git a/test/test-weof.c b/test/test-weof.c index 52c7afbd65b9..68e7cd45792d 100644 --- a/test/test-weof.c +++ b/test/test-weof.c @@ -99,10 +99,10 @@ main(int argc, char **argv) if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) return (1); - /* Initalize the event library */ + /* Initialize the event library */ event_init(); - /* Initalize one event */ + /* Initialize one event */ event_set(&ev, pair[1], EV_WRITE, write_cb, &ev); event_add(&ev, NULL); diff --git a/test/test.sh b/test/test.sh index dd3d98673cf2..f91a8b044598 100755 --- a/test/test.sh +++ b/test/test.sh @@ -82,8 +82,8 @@ run_tests () { fi done announce_n " test-dumpevents: " - if python2 -c 'import sys; assert(sys.version_info >= (2, 4))' 2>/dev/null && test -f $TEST_SRC_DIR/check-dumpevents.py; then - if $TEST_DIR/test-dumpevents | python2 $TEST_SRC_DIR/check-dumpevents.py >> "$TEST_OUTPUT_FILE" ; + if python -c 'import sys; assert(sys.version_info >= (2, 4))' 2>/dev/null && test -f $TEST_SRC_DIR/check-dumpevents.py; then + if $TEST_DIR/test-dumpevents | $TEST_SRC_DIR/check-dumpevents.py >> "$TEST_OUTPUT_FILE" ; then announce OKAY ; else diff --git a/test/tinytest.c b/test/tinytest.c index 3a8e33105501..85dfe74a720e 100644 --- a/test/tinytest.c +++ b/test/tinytest.c @@ -60,6 +60,8 @@ #include "tinytest_macros.h" #define LONGEST_TEST_NAME 16384 +#define DEFAULT_TESTCASE_TIMEOUT 30U +#define MAGIC_EXITCODE 42 static int in_tinytest_main = 0; /**< true if we're in tinytest_main().*/ static int n_ok = 0; /**< Number of tests that have passed */ @@ -69,6 +71,7 @@ static int n_skipped = 0; /**< Number of tests that have been skipped. */ static int opt_forked = 0; /**< True iff we're called from inside a win32 fork*/ static int opt_nofork = 0; /**< Suppress calls to fork() for debugging. */ static int opt_verbosity = 1; /**< -==quiet,0==terse,1==normal,2==verbose */ +static unsigned int opt_timeout = DEFAULT_TESTCASE_TIMEOUT; /**< Timeout for every test (using alarm()) */ const char *verbosity_flag = ""; const struct testlist_alias_t *cfg_aliases=NULL; @@ -79,14 +82,73 @@ const char *cur_test_prefix = NULL; /**< prefix of the current test group */ /** Name of the current test, if we haven't logged is yet. Used for --quiet */ const char *cur_test_name = NULL; +static void usage(struct testgroup_t *groups, int list_groups) + __attribute__((noreturn)); +static int process_test_option(struct testgroup_t *groups, const char *test); + #ifdef _WIN32 /* Copy of argv[0] for win32. */ static char commandname[MAX_PATH+1]; -#endif -static void usage(struct testgroup_t *groups, int list_groups) - __attribute__((noreturn)); -static int process_test_option(struct testgroup_t *groups, const char *test); +struct timeout_thread_args { + const testcase_fn *fn; + void *env; +}; + +static DWORD WINAPI +timeout_thread_proc_(LPVOID arg) +{ + struct timeout_thread_args *args = arg; + (*(args->fn))(args->env); + ExitThread(cur_test_outcome == FAIL ? 1 : 0); +} + +static enum outcome +testcase_run_in_thread_(const struct testcase_t *testcase, void *env) +{ + /* We will never run testcase in a new thread when the + timeout is set to zero */ + assert(opt_timeout); + DWORD ret, tid; + HANDLE handle; + struct timeout_thread_args args = { + &(testcase->fn), + env + }; + + handle =CreateThread(NULL, 0, timeout_thread_proc_, + (LPVOID)&args, 0, &tid); + ret = WaitForSingleObject(handle, opt_timeout * 1000U); + if (ret == WAIT_OBJECT_0) { + ret = 0; + if (!GetExitCodeThread(handle, &ret)) { + printf("GetExitCodeThread failed\n"); + ret = 1; + } + } else if (ret == WAIT_TIMEOUT) { + printf("timeout\n"); + } else { + printf("Wait failed\n"); + } + CloseHandle(handle); + if (ret == 0) + return OK; + else if (ret == MAGIC_EXITCODE) + return SKIP; + else + return FAIL; +} +#else +static unsigned int testcase_set_timeout_(void) +{ + return alarm(opt_timeout); +} + +static unsigned int testcase_reset_timeout_(void) +{ + return alarm(0); +} +#endif static enum outcome testcase_run_bare_(const struct testcase_t *testcase) @@ -102,7 +164,19 @@ testcase_run_bare_(const struct testcase_t *testcase) } cur_test_outcome = OK; - testcase->fn(env); + { + if (opt_timeout) { +#ifdef _WIN32 + cur_test_outcome = testcase_run_in_thread_(testcase, env); +#else + testcase_set_timeout_(); + testcase->fn(env); + testcase_reset_timeout_(); +#endif + } else { + testcase->fn(env); + } + } outcome = cur_test_outcome; if (testcase->setup) { @@ -113,7 +187,6 @@ testcase_run_bare_(const struct testcase_t *testcase) return outcome; } -#define MAGIC_EXITCODE 42 #ifndef NO_FORKING @@ -134,7 +207,7 @@ testcase_run_forked_(const struct testgroup_t *group, char buffer[LONGEST_TEST_NAME+256]; STARTUPINFOA si; PROCESS_INFORMATION info; - DWORD exitcode; + DWORD ret; if (!in_tinytest_main) { printf("\nERROR. On Windows, testcase_run_forked_ must be" @@ -144,7 +217,7 @@ testcase_run_forked_(const struct testgroup_t *group, if (opt_verbosity>0) printf("[forking] "); - snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s %s%s", + snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s --timeout 0 %s%s", commandname, verbosity_flag, group->prefix, testcase->name); memset(&si, 0, sizeof(si)); @@ -155,15 +228,23 @@ testcase_run_forked_(const struct testgroup_t *group, 0, NULL, NULL, &si, &info); if (!ok) { printf("CreateProcess failed!\n"); - return 0; + return FAIL; + } + ret = WaitForSingleObject(info.hProcess, + (opt_timeout ? opt_timeout * 1000U : INFINITE)); + + if (ret == WAIT_OBJECT_0) { + GetExitCodeProcess(info.hProcess, &ret); + } else if (ret == WAIT_TIMEOUT) { + printf("timeout\n"); + } else { + printf("Wait failed\n"); } - WaitForSingleObject(info.hProcess, INFINITE); - GetExitCodeProcess(info.hProcess, &exitcode); CloseHandle(info.hProcess); CloseHandle(info.hThread); - if (exitcode == 0) + if (ret == 0) return OK; - else if (exitcode == MAGIC_EXITCODE) + else if (ret == MAGIC_EXITCODE) return SKIP; else return FAIL; @@ -198,7 +279,7 @@ testcase_run_forked_(const struct testgroup_t *group, return FAIL; /* unreachable */ } else { /* parent */ - int status, r; + int status, r, exitcode; char b[1]; /* Close this now, so that if the other side closes it, * our read fails. */ @@ -206,12 +287,20 @@ testcase_run_forked_(const struct testgroup_t *group, r = (int)read(outcome_pipe[0], b, 1); if (r == 0) { printf("[Lost connection!] "); - return 0; + return FAIL; } else if (r != 1) { perror("read outcome from pipe"); } waitpid(pid, &status, 0); + exitcode = WEXITSTATUS(status); close(outcome_pipe[0]); + if (opt_verbosity>1) + printf("%s%s: exited with %i (%i)\n", group->prefix, testcase->name, exitcode, status); + if (exitcode != 0) + { + printf("[atexit failure!] "); + return FAIL; + } return b[0]=='Y' ? OK : (b[0]=='S' ? SKIP : FAIL); } #endif @@ -253,15 +342,12 @@ testcase_run_one(const struct testgroup_t *group, } if (outcome == OK) { - ++n_ok; if (opt_verbosity>0 && !opt_forked) puts(opt_verbosity==1?"OK":""); } else if (outcome == SKIP) { - ++n_skipped; if (opt_verbosity>0 && !opt_forked) puts("SKIPPED"); } else { - ++n_bad; if (!opt_forked) printf("\n [%s FAILED]\n", testcase->name); } @@ -312,7 +398,7 @@ tinytest_set_flag_(struct testgroup_t *groups, const char *arg, int set, unsigne static void usage(struct testgroup_t *groups, int list_groups) { - puts("Options are: [--verbose|--quiet|--terse] [--no-fork]"); + puts("Options are: [--verbose|--quiet|--terse] [--no-fork] [--timeout <sec>]"); puts(" Specify tests by name, or using a prefix ending with '..'"); puts(" To skip a test, prefix its name with a colon."); puts(" To enable a disabled test, prefix its name with a plus."); @@ -409,8 +495,15 @@ tinytest_main(int c, const char **v, struct testgroup_t *groups) usage(groups, 0); } else if (!strcmp(v[i], "--list-tests")) { usage(groups, 1); + } else if (!strcmp(v[i], "--timeout")) { + ++i; + if (i >= c) { + fprintf(stderr, "--timeout requires argument\n"); + return -1; + } + opt_timeout = (unsigned)atoi(v[i]); } else { - printf("Unknown option %s. Try --help\n",v[i]); + fprintf(stderr, "Unknown option %s. Try --help\n", v[i]); return -1; } } else { @@ -428,11 +521,35 @@ tinytest_main(int c, const char **v, struct testgroup_t *groups) #endif ++in_tinytest_main; - for (i=0; groups[i].prefix; ++i) - for (j=0; groups[i].cases[j].name; ++j) - if (groups[i].cases[j].flags & TT_ENABLED_) - testcase_run_one(&groups[i], - &groups[i].cases[j]); + for (i = 0; groups[i].prefix; ++i) { + struct testgroup_t *group = &groups[i]; + for (j = 0; group->cases[j].name; ++j) { + struct testcase_t *testcase = &group->cases[j]; + int test_attempts = 3; + int test_ret_err; + + if (!(testcase->flags & TT_ENABLED_)) + continue; + + for (;;) { + test_ret_err = testcase_run_one(group, testcase); + + if (test_ret_err == OK) + break; + if (!(testcase->flags & TT_RETRIABLE)) + break; + printf("\n [RETRYING %s (%i)]\n", testcase->name, test_attempts); + if (!test_attempts--) + break; + } + + switch (test_ret_err) { + case OK: ++n_ok; break; + case SKIP: ++n_skipped; break; + default: ++n_bad; break; + } + } + } --in_tinytest_main; @@ -462,7 +579,7 @@ tinytest_set_test_failed_(void) printf("%s%s: ", cur_test_prefix, cur_test_name); cur_test_name = NULL; } - cur_test_outcome = 0; + cur_test_outcome = FAIL; } void diff --git a/test/tinytest.h b/test/tinytest.h index ed07b26bc006..d321dd467542 100644 --- a/test/tinytest.h +++ b/test/tinytest.h @@ -34,8 +34,11 @@ #define TT_ENABLED_ (1<<2) /** Flag for a test that's off by default. */ #define TT_OFF_BY_DEFAULT (1<<3) +/** Flag for a test that should be runned again in case of failure (but not + * more then 3 times). */ +#define TT_RETRIABLE (1<<4) /** If you add your own flags, make them start at this point. */ -#define TT_FIRST_USER_FLAG (1<<4) +#define TT_FIRST_USER_FLAG (1<<5) typedef void (*testcase_fn)(void *); diff --git a/test/tinytest_macros.h b/test/tinytest_macros.h index 2c02a741b164..e01f5d56cb33 100644 --- a/test/tinytest_macros.h +++ b/test/tinytest_macros.h @@ -113,8 +113,8 @@ #define tt_assert_test_fmt_type(a,b,str_test,type,test,printf_type,printf_fmt, \ setup_block,cleanup_block,die_on_fail) \ TT_STMT_BEGIN \ - type val1_ = (a); \ - type val2_ = (b); \ + type val1_ = (type)(a); \ + type val2_ = (type)(b); \ int tt_status_ = (test); \ if (!tt_status_ || tinytest_get_verbosity_()>1) { \ printf_type print_; \ @@ -158,6 +158,14 @@ tt_assert_test_type(a,b,#a" "#op" "#b,long,(val1_ op val2_), \ "%ld",TT_EXIT_TEST_FUNCTION) +/** To compare SOCKET(windows)/fd */ +#define tt_fd_op(a,op,b) do { \ + int _a = (int)(a); \ + int _b = (int)(b); \ + tt_assert_test_type(_a,_b,#a" "#op" "#b,long,(val1_ op val2_), \ + "%ld",TT_EXIT_TEST_FUNCTION); \ +} while (0) + #define tt_uint_op(a,op,b) \ tt_assert_test_type(a,b,#a" "#op" "#b,unsigned long, \ (val1_ op val2_),"%lu",TT_EXIT_TEST_FUNCTION) |