|  | /* | 
|  | * Copyright (c) 2003, 2004 Niels Provos <provos@citi.umich.edu> | 
|  | * All rights reserved. | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions | 
|  | * are met: | 
|  | * 1. Redistributions of source code must retain the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer. | 
|  | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer in the | 
|  | *    documentation and/or other materials provided with the distribution. | 
|  | * 3. The name of the author may not be used to endorse or promote products | 
|  | *    derived from this software without specific prior written permission. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | 
|  | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | 
|  | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | 
|  | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | 
|  | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | 
|  | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
|  | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
|  | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 
|  | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | */ | 
|  |  | 
|  | #ifdef WIN32 | 
|  | #include <winsock2.h> | 
|  | #include <windows.h> | 
|  | #endif | 
|  |  | 
|  | #ifdef HAVE_CONFIG_H | 
|  | #include "config.h" | 
|  | #endif | 
|  |  | 
|  | #include <sys/types.h> | 
|  | #include <sys/stat.h> | 
|  | #ifdef HAVE_SYS_TIME_H | 
|  | #include <sys/time.h> | 
|  | #endif | 
|  | #include <sys/queue.h> | 
|  | #ifndef WIN32 | 
|  | #include <sys/socket.h> | 
|  | #include <sys/wait.h> | 
|  | #include <signal.h> | 
|  | #include <unistd.h> | 
|  | #include <netdb.h> | 
|  | #endif | 
|  | #include <assert.h> | 
|  | #include <fcntl.h> | 
|  | #include <signal.h> | 
|  | #include <stdlib.h> | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #include <errno.h> | 
|  |  | 
|  | #include "event.h" | 
|  | #include "evutil.h" | 
|  | #include "event-internal.h" | 
|  | #include "log.h" | 
|  |  | 
|  | #include "regress.h" | 
|  | #ifndef WIN32 | 
|  | #include "regress.gen.h" | 
|  | #endif | 
|  |  | 
|  | int pair[2]; | 
|  | int test_ok; | 
|  | static int called; | 
|  | static char wbuf[4096]; | 
|  | static char rbuf[4096]; | 
|  | static int woff; | 
|  | static int roff; | 
|  | static int usepersist; | 
|  | static struct timeval tset; | 
|  | static struct timeval tcalled; | 
|  | static struct event_base *global_base; | 
|  |  | 
|  | #define TEST1	"this is a test" | 
|  | #define SECONDS	1 | 
|  |  | 
|  | #ifndef SHUT_WR | 
|  | #define SHUT_WR 1 | 
|  | #endif | 
|  |  | 
|  | #ifdef WIN32 | 
|  | #define write(fd,buf,len) send((fd),(buf),(len),0) | 
|  | #define read(fd,buf,len) recv((fd),(buf),(len),0) | 
|  | #endif | 
|  |  | 
|  | static void | 
|  | simple_read_cb(int fd, short event, void *arg) | 
|  | { | 
|  | char buf[256]; | 
|  | int len; | 
|  |  | 
|  | if (arg == NULL) | 
|  | return; | 
|  |  | 
|  | len = read(fd, buf, sizeof(buf)); | 
|  |  | 
|  | if (len) { | 
|  | if (!called) { | 
|  | if (event_add(arg, NULL) == -1) | 
|  | exit(1); | 
|  | } | 
|  | } else if (called == 1) | 
|  | test_ok = 1; | 
|  |  | 
|  | called++; | 
|  | } | 
|  |  | 
|  | static void | 
|  | simple_write_cb(int fd, short event, void *arg) | 
|  | { | 
|  | int len; | 
|  |  | 
|  | if (arg == NULL) | 
|  | return; | 
|  |  | 
|  | len = write(fd, TEST1, strlen(TEST1) + 1); | 
|  | if (len == -1) | 
|  | test_ok = 0; | 
|  | else | 
|  | test_ok = 1; | 
|  | } | 
|  |  | 
|  | static void | 
|  | multiple_write_cb(int fd, short event, void *arg) | 
|  | { | 
|  | struct event *ev = arg; | 
|  | int len; | 
|  |  | 
|  | len = 128; | 
|  | if (woff + len >= sizeof(wbuf)) | 
|  | len = sizeof(wbuf) - woff; | 
|  |  | 
|  | len = write(fd, wbuf + woff, len); | 
|  | if (len == -1) { | 
|  | fprintf(stderr, "%s: write\n", __func__); | 
|  | if (usepersist) | 
|  | event_del(ev); | 
|  | return; | 
|  | } | 
|  |  | 
|  | woff += len; | 
|  |  | 
|  | if (woff >= sizeof(wbuf)) { | 
|  | shutdown(fd, SHUT_WR); | 
|  | if (usepersist) | 
|  | event_del(ev); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!usepersist) { | 
|  | if (event_add(ev, NULL) == -1) | 
|  | exit(1); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | multiple_read_cb(int fd, short event, void *arg) | 
|  | { | 
|  | struct event *ev = arg; | 
|  | int len; | 
|  |  | 
|  | len = read(fd, rbuf + roff, sizeof(rbuf) - roff); | 
|  | if (len == -1) | 
|  | fprintf(stderr, "%s: read\n", __func__); | 
|  | if (len <= 0) { | 
|  | if (usepersist) | 
|  | event_del(ev); | 
|  | return; | 
|  | } | 
|  |  | 
|  | roff += len; | 
|  | if (!usepersist) { | 
|  | if (event_add(ev, NULL) == -1) | 
|  | exit(1); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | timeout_cb(int fd, short event, void *arg) | 
|  | { | 
|  | struct timeval tv; | 
|  | int diff; | 
|  |  | 
|  | evutil_gettimeofday(&tcalled, NULL); | 
|  | if (evutil_timercmp(&tcalled, &tset, >)) | 
|  | evutil_timersub(&tcalled, &tset, &tv); | 
|  | else | 
|  | evutil_timersub(&tset, &tcalled, &tv); | 
|  |  | 
|  | diff = tv.tv_sec*1000 + tv.tv_usec/1000 - SECONDS * 1000; | 
|  | if (diff < 0) | 
|  | diff = -diff; | 
|  |  | 
|  | if (diff < 100) | 
|  | test_ok = 1; | 
|  | } | 
|  |  | 
|  | #ifndef WIN32 | 
|  | static void | 
|  | signal_cb_sa(int sig) | 
|  | { | 
|  | test_ok = 2; | 
|  | } | 
|  |  | 
|  | static void | 
|  | signal_cb(int fd, short event, void *arg) | 
|  | { | 
|  | struct event *ev = arg; | 
|  |  | 
|  | signal_del(ev); | 
|  | test_ok = 1; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | struct both { | 
|  | struct event ev; | 
|  | int nread; | 
|  | }; | 
|  |  | 
|  | static void | 
|  | combined_read_cb(int fd, short event, void *arg) | 
|  | { | 
|  | struct both *both = arg; | 
|  | char buf[128]; | 
|  | int len; | 
|  |  | 
|  | len = read(fd, buf, sizeof(buf)); | 
|  | if (len == -1) | 
|  | fprintf(stderr, "%s: read\n", __func__); | 
|  | if (len <= 0) | 
|  | return; | 
|  |  | 
|  | both->nread += len; | 
|  | if (event_add(&both->ev, NULL) == -1) | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | static void | 
|  | combined_write_cb(int fd, short event, void *arg) | 
|  | { | 
|  | struct both *both = arg; | 
|  | char buf[128]; | 
|  | int len; | 
|  |  | 
|  | len = sizeof(buf); | 
|  | if (len > both->nread) | 
|  | len = both->nread; | 
|  |  | 
|  | len = write(fd, buf, len); | 
|  | if (len == -1) | 
|  | fprintf(stderr, "%s: write\n", __func__); | 
|  | if (len <= 0) { | 
|  | shutdown(fd, SHUT_WR); | 
|  | return; | 
|  | } | 
|  |  | 
|  | both->nread -= len; | 
|  | if (event_add(&both->ev, NULL) == -1) | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | /* Test infrastructure */ | 
|  |  | 
|  | static int | 
|  | setup_test(const char *name) | 
|  | { | 
|  |  | 
|  | fprintf(stdout, "%s", name); | 
|  |  | 
|  | if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) { | 
|  | fprintf(stderr, "%s: socketpair\n", __func__); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | #ifdef HAVE_FCNTL | 
|  | if (fcntl(pair[0], F_SETFL, O_NONBLOCK) == -1) | 
|  | fprintf(stderr, "fcntl(O_NONBLOCK)"); | 
|  |  | 
|  | if (fcntl(pair[1], F_SETFL, O_NONBLOCK) == -1) | 
|  | fprintf(stderr, "fcntl(O_NONBLOCK)"); | 
|  | #endif | 
|  |  | 
|  | test_ok = 0; | 
|  | called = 0; | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | static int | 
|  | cleanup_test(void) | 
|  | { | 
|  | #ifndef WIN32 | 
|  | close(pair[0]); | 
|  | close(pair[1]); | 
|  | #else | 
|  | CloseHandle((HANDLE)pair[0]); | 
|  | CloseHandle((HANDLE)pair[1]); | 
|  | #endif | 
|  | if (test_ok) | 
|  | fprintf(stdout, "OK\n"); | 
|  | else { | 
|  | fprintf(stdout, "FAILED\n"); | 
|  | exit(1); | 
|  | } | 
|  | test_ok = 0; | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_registerfds(void) | 
|  | { | 
|  | int i, j; | 
|  | int pair[2]; | 
|  | struct event read_evs[512]; | 
|  | struct event write_evs[512]; | 
|  |  | 
|  | struct event_base *base = event_base_new(); | 
|  |  | 
|  | fprintf(stdout, "Testing register fds: "); | 
|  |  | 
|  | for (i = 0; i < 512; ++i) { | 
|  | if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) { | 
|  | /* run up to the limit of file descriptors */ | 
|  | break; | 
|  | } | 
|  | event_set(&read_evs[i], pair[0], | 
|  | EV_READ|EV_PERSIST, simple_read_cb, NULL); | 
|  | event_base_set(base, &read_evs[i]); | 
|  | event_add(&read_evs[i], NULL); | 
|  | event_set(&write_evs[i], pair[1], | 
|  | EV_WRITE|EV_PERSIST, simple_write_cb, NULL); | 
|  | event_base_set(base, &write_evs[i]); | 
|  | event_add(&write_evs[i], NULL); | 
|  |  | 
|  | /* just loop once */ | 
|  | event_base_loop(base, EVLOOP_ONCE); | 
|  | } | 
|  |  | 
|  | /* now delete everything */ | 
|  | for (j = 0; j < i; ++j) { | 
|  | event_del(&read_evs[j]); | 
|  | event_del(&write_evs[j]); | 
|  | #ifndef WIN32 | 
|  | close(read_evs[j].ev_fd); | 
|  | close(write_evs[j].ev_fd); | 
|  | #else | 
|  | CloseHandle((HANDLE)read_evs[j].ev_fd); | 
|  | CloseHandle((HANDLE)write_evs[j].ev_fd); | 
|  | #endif | 
|  |  | 
|  | /* just loop once */ | 
|  | event_base_loop(base, EVLOOP_ONCE); | 
|  | } | 
|  |  | 
|  | event_base_free(base); | 
|  |  | 
|  | fprintf(stdout, "OK\n"); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_simpleread(void) | 
|  | { | 
|  | struct event ev; | 
|  |  | 
|  | /* Very simple read test */ | 
|  | setup_test("Simple read: "); | 
|  |  | 
|  | write(pair[0], TEST1, strlen(TEST1)+1); | 
|  | shutdown(pair[0], SHUT_WR); | 
|  |  | 
|  | event_set(&ev, pair[1], EV_READ, simple_read_cb, &ev); | 
|  | if (event_add(&ev, NULL) == -1) | 
|  | exit(1); | 
|  | event_dispatch(); | 
|  |  | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_simplewrite(void) | 
|  | { | 
|  | struct event ev; | 
|  |  | 
|  | /* Very simple write test */ | 
|  | setup_test("Simple write: "); | 
|  |  | 
|  | event_set(&ev, pair[0], EV_WRITE, simple_write_cb, &ev); | 
|  | if (event_add(&ev, NULL) == -1) | 
|  | exit(1); | 
|  | event_dispatch(); | 
|  |  | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_multiple(void) | 
|  | { | 
|  | struct event ev, ev2; | 
|  | int i; | 
|  |  | 
|  | /* Multiple read and write test */ | 
|  | setup_test("Multiple read/write: "); | 
|  | memset(rbuf, 0, sizeof(rbuf)); | 
|  | for (i = 0; i < sizeof(wbuf); i++) | 
|  | wbuf[i] = i; | 
|  |  | 
|  | roff = woff = 0; | 
|  | usepersist = 0; | 
|  |  | 
|  | event_set(&ev, pair[0], EV_WRITE, multiple_write_cb, &ev); | 
|  | if (event_add(&ev, NULL) == -1) | 
|  | exit(1); | 
|  | event_set(&ev2, pair[1], EV_READ, multiple_read_cb, &ev2); | 
|  | if (event_add(&ev2, NULL) == -1) | 
|  | exit(1); | 
|  | event_dispatch(); | 
|  |  | 
|  | if (roff == woff) | 
|  | test_ok = memcmp(rbuf, wbuf, sizeof(wbuf)) == 0; | 
|  |  | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_persistent(void) | 
|  | { | 
|  | struct event ev, ev2; | 
|  | int i; | 
|  |  | 
|  | /* Multiple read and write test with persist */ | 
|  | setup_test("Persist read/write: "); | 
|  | memset(rbuf, 0, sizeof(rbuf)); | 
|  | for (i = 0; i < sizeof(wbuf); i++) | 
|  | wbuf[i] = i; | 
|  |  | 
|  | roff = woff = 0; | 
|  | usepersist = 1; | 
|  |  | 
|  | event_set(&ev, pair[0], EV_WRITE|EV_PERSIST, multiple_write_cb, &ev); | 
|  | if (event_add(&ev, NULL) == -1) | 
|  | exit(1); | 
|  | event_set(&ev2, pair[1], EV_READ|EV_PERSIST, multiple_read_cb, &ev2); | 
|  | if (event_add(&ev2, NULL) == -1) | 
|  | exit(1); | 
|  | event_dispatch(); | 
|  |  | 
|  | if (roff == woff) | 
|  | test_ok = memcmp(rbuf, wbuf, sizeof(wbuf)) == 0; | 
|  |  | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_combined(void) | 
|  | { | 
|  | struct both r1, r2, w1, w2; | 
|  |  | 
|  | setup_test("Combined read/write: "); | 
|  | memset(&r1, 0, sizeof(r1)); | 
|  | memset(&r2, 0, sizeof(r2)); | 
|  | memset(&w1, 0, sizeof(w1)); | 
|  | memset(&w2, 0, sizeof(w2)); | 
|  |  | 
|  | w1.nread = 4096; | 
|  | w2.nread = 8192; | 
|  |  | 
|  | event_set(&r1.ev, pair[0], EV_READ, combined_read_cb, &r1); | 
|  | event_set(&w1.ev, pair[0], EV_WRITE, combined_write_cb, &w1); | 
|  | event_set(&r2.ev, pair[1], EV_READ, combined_read_cb, &r2); | 
|  | event_set(&w2.ev, pair[1], EV_WRITE, combined_write_cb, &w2); | 
|  | if (event_add(&r1.ev, NULL) == -1) | 
|  | exit(1); | 
|  | if (event_add(&w1.ev, NULL)) | 
|  | exit(1); | 
|  | if (event_add(&r2.ev, NULL)) | 
|  | exit(1); | 
|  | if (event_add(&w2.ev, NULL)) | 
|  | exit(1); | 
|  |  | 
|  | event_dispatch(); | 
|  |  | 
|  | if (r1.nread == 8192 && r2.nread == 4096) | 
|  | test_ok = 1; | 
|  |  | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_simpletimeout(void) | 
|  | { | 
|  | struct timeval tv; | 
|  | struct event ev; | 
|  |  | 
|  | setup_test("Simple timeout: "); | 
|  |  | 
|  | tv.tv_usec = 0; | 
|  | tv.tv_sec = SECONDS; | 
|  | evtimer_set(&ev, timeout_cb, NULL); | 
|  | evtimer_add(&ev, &tv); | 
|  |  | 
|  | evutil_gettimeofday(&tset, NULL); | 
|  | event_dispatch(); | 
|  |  | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | #ifndef WIN32 | 
|  | extern struct event_base *current_base; | 
|  |  | 
|  | static void | 
|  | child_signal_cb(int fd, short event, void *arg) | 
|  | { | 
|  | struct timeval tv; | 
|  | int *pint = arg; | 
|  |  | 
|  | *pint = 1; | 
|  |  | 
|  | tv.tv_usec = 500000; | 
|  | tv.tv_sec = 0; | 
|  | event_loopexit(&tv); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_fork(void) | 
|  | { | 
|  | int status, got_sigchld = 0; | 
|  | struct event ev, sig_ev; | 
|  | pid_t pid; | 
|  |  | 
|  | setup_test("After fork: "); | 
|  |  | 
|  | write(pair[0], TEST1, strlen(TEST1)+1); | 
|  |  | 
|  | event_set(&ev, pair[1], EV_READ, simple_read_cb, &ev); | 
|  | if (event_add(&ev, NULL) == -1) | 
|  | exit(1); | 
|  |  | 
|  | signal_set(&sig_ev, SIGCHLD, child_signal_cb, &got_sigchld); | 
|  | signal_add(&sig_ev, NULL); | 
|  |  | 
|  | if ((pid = fork()) == 0) { | 
|  | /* in the child */ | 
|  | if (event_reinit(current_base) == -1) { | 
|  | fprintf(stderr, "FAILED (reinit)\n"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | signal_del(&sig_ev); | 
|  |  | 
|  | called = 0; | 
|  |  | 
|  | event_dispatch(); | 
|  |  | 
|  | /* 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); | 
|  | } | 
|  |  | 
|  | /* wait for the child to read the data */ | 
|  | sleep(1); | 
|  |  | 
|  | write(pair[0], TEST1, strlen(TEST1)+1); | 
|  |  | 
|  | if (waitpid(pid, &status, 0) == -1) { | 
|  | fprintf(stderr, "FAILED (fork)\n"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | if (WEXITSTATUS(status) != 76) { | 
|  | fprintf(stderr, "FAILED (exit): %d\n", WEXITSTATUS(status)); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | /* test that the current event loop still works */ | 
|  | write(pair[0], TEST1, strlen(TEST1)+1); | 
|  | shutdown(pair[0], SHUT_WR); | 
|  |  | 
|  | event_dispatch(); | 
|  |  | 
|  | if (!got_sigchld) { | 
|  | fprintf(stdout, "FAILED (sigchld)\n"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | signal_del(&sig_ev); | 
|  |  | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_simplesignal(void) | 
|  | { | 
|  | struct event ev; | 
|  | struct itimerval itv; | 
|  |  | 
|  | setup_test("Simple signal: "); | 
|  | signal_set(&ev, SIGALRM, signal_cb, &ev); | 
|  | signal_add(&ev, NULL); | 
|  | /* find bugs in which operations are re-ordered */ | 
|  | signal_del(&ev); | 
|  | signal_add(&ev, NULL); | 
|  |  | 
|  | memset(&itv, 0, sizeof(itv)); | 
|  | itv.it_value.tv_sec = 1; | 
|  | if (setitimer(ITIMER_REAL, &itv, NULL) == -1) | 
|  | goto skip_simplesignal; | 
|  |  | 
|  | event_dispatch(); | 
|  | skip_simplesignal: | 
|  | if (signal_del(&ev) == -1) | 
|  | test_ok = 0; | 
|  |  | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_multiplesignal(void) | 
|  | { | 
|  | struct event ev_one, ev_two; | 
|  | struct itimerval itv; | 
|  |  | 
|  | setup_test("Multiple signal: "); | 
|  |  | 
|  | signal_set(&ev_one, SIGALRM, signal_cb, &ev_one); | 
|  | signal_add(&ev_one, NULL); | 
|  |  | 
|  | signal_set(&ev_two, SIGALRM, signal_cb, &ev_two); | 
|  | signal_add(&ev_two, NULL); | 
|  |  | 
|  | memset(&itv, 0, sizeof(itv)); | 
|  | itv.it_value.tv_sec = 1; | 
|  | if (setitimer(ITIMER_REAL, &itv, NULL) == -1) | 
|  | goto skip_simplesignal; | 
|  |  | 
|  | event_dispatch(); | 
|  |  | 
|  | skip_simplesignal: | 
|  | if (signal_del(&ev_one) == -1) | 
|  | test_ok = 0; | 
|  | if (signal_del(&ev_two) == -1) | 
|  | test_ok = 0; | 
|  |  | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_immediatesignal(void) | 
|  | { | 
|  | struct event ev; | 
|  |  | 
|  | test_ok = 0; | 
|  | printf("Immediate signal: "); | 
|  | signal_set(&ev, SIGUSR1, signal_cb, &ev); | 
|  | signal_add(&ev, NULL); | 
|  | raise(SIGUSR1); | 
|  | event_loop(EVLOOP_NONBLOCK); | 
|  | signal_del(&ev); | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_signal_dealloc(void) | 
|  | { | 
|  | /* make sure that signal_event is event_del'ed and pipe closed */ | 
|  | struct event ev; | 
|  | struct event_base *base = event_init(); | 
|  | printf("Signal dealloc: "); | 
|  | signal_set(&ev, SIGUSR1, signal_cb, &ev); | 
|  | signal_add(&ev, NULL); | 
|  | signal_del(&ev); | 
|  | event_base_free(base); | 
|  | /* If we got here without asserting, we're fine. */ | 
|  | test_ok = 1; | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_signal_pipeloss(void) | 
|  | { | 
|  | /* make sure that the base1 pipe is closed correctly. */ | 
|  | struct event_base *base1, *base2; | 
|  | int pipe1; | 
|  | test_ok = 0; | 
|  | printf("Signal pipeloss: "); | 
|  | base1 = event_init(); | 
|  | pipe1 = base1->sig.ev_signal_pair[0]; | 
|  | base2 = event_init(); | 
|  | event_base_free(base2); | 
|  | event_base_free(base1); | 
|  | if (close(pipe1) != -1 || errno!=EBADF) { | 
|  | /* fd must be closed, so second close gives -1, EBADF */ | 
|  | printf("signal pipe not closed. "); | 
|  | test_ok = 0; | 
|  | } else { | 
|  | test_ok = 1; | 
|  | } | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * make two bases to catch signals, use both of them.  this only works | 
|  | * for event mechanisms that use our signal pipe trick.  kqueue handles | 
|  | * signals internally, and all interested kqueues get all the signals. | 
|  | */ | 
|  | static void | 
|  | test_signal_switchbase(void) | 
|  | { | 
|  | struct event ev1, ev2; | 
|  | struct event_base *base1, *base2; | 
|  | int is_kqueue; | 
|  | test_ok = 0; | 
|  | printf("Signal switchbase: "); | 
|  | base1 = event_init(); | 
|  | base2 = event_init(); | 
|  | is_kqueue = !strcmp(event_get_method(),"kqueue"); | 
|  | signal_set(&ev1, SIGUSR1, signal_cb, &ev1); | 
|  | signal_set(&ev2, SIGUSR1, signal_cb, &ev2); | 
|  | if (event_base_set(base1, &ev1) || | 
|  | event_base_set(base2, &ev2) || | 
|  | event_add(&ev1, NULL) || | 
|  | event_add(&ev2, NULL)) { | 
|  | fprintf(stderr, "%s: cannot set base, add\n", __func__); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | test_ok = 0; | 
|  | /* can handle signal before loop is called */ | 
|  | raise(SIGUSR1); | 
|  | event_base_loop(base2, EVLOOP_NONBLOCK); | 
|  | if (is_kqueue) { | 
|  | if (!test_ok) | 
|  | goto done; | 
|  | test_ok = 0; | 
|  | } | 
|  | event_base_loop(base1, EVLOOP_NONBLOCK); | 
|  | if (test_ok && !is_kqueue) { | 
|  | test_ok = 0; | 
|  |  | 
|  | /* set base1 to handle signals */ | 
|  | event_base_loop(base1, EVLOOP_NONBLOCK); | 
|  | raise(SIGUSR1); | 
|  | event_base_loop(base1, EVLOOP_NONBLOCK); | 
|  | event_base_loop(base2, EVLOOP_NONBLOCK); | 
|  | } | 
|  | done: | 
|  | event_base_free(base1); | 
|  | event_base_free(base2); | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * assert that a signal event removed from the event queue really is | 
|  | * removed - with no possibility of it's parent handler being fired. | 
|  | */ | 
|  | static void | 
|  | test_signal_assert(void) | 
|  | { | 
|  | struct event ev; | 
|  | struct event_base *base = event_init(); | 
|  | test_ok = 0; | 
|  | printf("Signal handler assert: "); | 
|  | /* use SIGCONT so we don't kill ourselves when we signal to nowhere */ | 
|  | signal_set(&ev, SIGCONT, signal_cb, &ev); | 
|  | signal_add(&ev, NULL); | 
|  | /* | 
|  | * if signal_del() fails to reset the handler, it's current handler | 
|  | * will still point to evsignal_handler(). | 
|  | */ | 
|  | signal_del(&ev); | 
|  |  | 
|  | raise(SIGCONT); | 
|  | /* only way to verify we were in evsignal_handler() */ | 
|  | if (base->sig.evsignal_caught) | 
|  | test_ok = 0; | 
|  | else | 
|  | test_ok = 1; | 
|  |  | 
|  | event_base_free(base); | 
|  | cleanup_test(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * assert that we restore our previous signal handler properly. | 
|  | */ | 
|  | static void | 
|  | test_signal_restore(void) | 
|  | { | 
|  | struct event ev; | 
|  | struct event_base *base = event_init(); | 
|  | #ifdef HAVE_SIGACTION | 
|  | struct sigaction sa; | 
|  | #endif | 
|  |  | 
|  | test_ok = 0; | 
|  | printf("Signal handler restore: "); | 
|  | #ifdef HAVE_SIGACTION | 
|  | sa.sa_handler = signal_cb_sa; | 
|  | sa.sa_flags = 0x0; | 
|  | sigemptyset(&sa.sa_mask); | 
|  | if (sigaction(SIGUSR1, &sa, NULL) == -1) | 
|  | goto out; | 
|  | #else | 
|  | if (signal(SIGUSR1, signal_cb_sa) == SIG_ERR) | 
|  | goto out; | 
|  | #endif | 
|  | signal_set(&ev, SIGUSR1, signal_cb, &ev); | 
|  | signal_add(&ev, NULL); | 
|  | signal_del(&ev); | 
|  |  | 
|  | raise(SIGUSR1); | 
|  | /* 1 == signal_cb, 2 == signal_cb_sa, we want our previous handler */ | 
|  | if (test_ok != 2) | 
|  | test_ok = 0; | 
|  | out: | 
|  | event_base_free(base); | 
|  | cleanup_test(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | static void | 
|  | signal_cb_swp(int sig, short event, void *arg) | 
|  | { | 
|  | called++; | 
|  | if (called < 5) | 
|  | raise(sig); | 
|  | else | 
|  | event_loopexit(NULL); | 
|  | } | 
|  | static void | 
|  | timeout_cb_swp(int fd, short event, void *arg) | 
|  | { | 
|  | if (called == -1) { | 
|  | struct timeval tv = {5, 0}; | 
|  |  | 
|  | called = 0; | 
|  | evtimer_add((struct event *)arg, &tv); | 
|  | raise(SIGUSR1); | 
|  | return; | 
|  | } | 
|  | test_ok = 0; | 
|  | event_loopexit(NULL); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_signal_while_processing(void) | 
|  | { | 
|  | struct event_base *base = event_init(); | 
|  | struct event ev, ev_timer; | 
|  | struct timeval tv = {0, 0}; | 
|  |  | 
|  | setup_test("Receiving a signal while processing other signal: "); | 
|  |  | 
|  | called = -1; | 
|  | test_ok = 1; | 
|  | signal_set(&ev, SIGUSR1, signal_cb_swp, NULL); | 
|  | signal_add(&ev, NULL); | 
|  | evtimer_set(&ev_timer, timeout_cb_swp, &ev_timer); | 
|  | evtimer_add(&ev_timer, &tv); | 
|  | event_dispatch(); | 
|  |  | 
|  | event_base_free(base); | 
|  | cleanup_test(); | 
|  | return; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static void | 
|  | test_free_active_base(void) | 
|  | { | 
|  | struct event_base *base1; | 
|  | struct event ev1; | 
|  | setup_test("Free active base: "); | 
|  | base1 = event_init(); | 
|  | event_set(&ev1, pair[1], EV_READ, simple_read_cb, &ev1); | 
|  | event_base_set(base1, &ev1); | 
|  | event_add(&ev1, NULL); | 
|  | /* event_del(&ev1); */ | 
|  | event_base_free(base1); | 
|  | test_ok = 1; | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_event_base_new(void) | 
|  | { | 
|  | struct event_base *base; | 
|  | struct event ev1; | 
|  | setup_test("Event base new: "); | 
|  |  | 
|  | write(pair[0], TEST1, strlen(TEST1)+1); | 
|  | shutdown(pair[0], SHUT_WR); | 
|  |  | 
|  | base = event_base_new(); | 
|  | event_set(&ev1, pair[1], EV_READ, simple_read_cb, &ev1); | 
|  | event_base_set(base, &ev1); | 
|  | event_add(&ev1, NULL); | 
|  |  | 
|  | event_base_dispatch(base); | 
|  |  | 
|  | event_base_free(base); | 
|  | test_ok = 1; | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_loopexit(void) | 
|  | { | 
|  | struct timeval tv, tv_start, tv_end; | 
|  | struct event ev; | 
|  |  | 
|  | setup_test("Loop exit: "); | 
|  |  | 
|  | tv.tv_usec = 0; | 
|  | tv.tv_sec = 60*60*24; | 
|  | evtimer_set(&ev, timeout_cb, NULL); | 
|  | evtimer_add(&ev, &tv); | 
|  |  | 
|  | tv.tv_usec = 0; | 
|  | tv.tv_sec = 1; | 
|  | event_loopexit(&tv); | 
|  |  | 
|  | evutil_gettimeofday(&tv_start, NULL); | 
|  | event_dispatch(); | 
|  | evutil_gettimeofday(&tv_end, NULL); | 
|  | evutil_timersub(&tv_end, &tv_start, &tv_end); | 
|  |  | 
|  | evtimer_del(&ev); | 
|  |  | 
|  | if (tv.tv_sec < 2) | 
|  | test_ok = 1; | 
|  |  | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_loopexit_multiple(void) | 
|  | { | 
|  | struct timeval tv; | 
|  | struct event_base *base; | 
|  |  | 
|  | setup_test("Loop Multiple exit: "); | 
|  |  | 
|  | base = event_base_new(); | 
|  |  | 
|  | tv.tv_usec = 0; | 
|  | tv.tv_sec = 1; | 
|  | event_base_loopexit(base, &tv); | 
|  |  | 
|  | tv.tv_usec = 0; | 
|  | tv.tv_sec = 2; | 
|  | event_base_loopexit(base, &tv); | 
|  |  | 
|  | event_base_dispatch(base); | 
|  |  | 
|  | event_base_free(base); | 
|  |  | 
|  | test_ok = 1; | 
|  |  | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | static void | 
|  | break_cb(int fd, short events, void *arg) | 
|  | { | 
|  | test_ok = 1; | 
|  | event_loopbreak(); | 
|  | } | 
|  |  | 
|  | static void | 
|  | fail_cb(int fd, short events, void *arg) | 
|  | { | 
|  | test_ok = 0; | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_loopbreak(void) | 
|  | { | 
|  | struct event ev1, ev2; | 
|  | struct timeval tv; | 
|  |  | 
|  | setup_test("Loop break: "); | 
|  |  | 
|  | tv.tv_sec = 0; | 
|  | tv.tv_usec = 0; | 
|  | evtimer_set(&ev1, break_cb, NULL); | 
|  | evtimer_add(&ev1, &tv); | 
|  | evtimer_set(&ev2, fail_cb, NULL); | 
|  | evtimer_add(&ev2, &tv); | 
|  |  | 
|  | event_dispatch(); | 
|  |  | 
|  | evtimer_del(&ev1); | 
|  | evtimer_del(&ev2); | 
|  |  | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_evbuffer(void) { | 
|  |  | 
|  | struct evbuffer *evb = evbuffer_new(); | 
|  | setup_test("Testing Evbuffer: "); | 
|  |  | 
|  | evbuffer_add_printf(evb, "%s/%d", "hello", 1); | 
|  |  | 
|  | if (EVBUFFER_LENGTH(evb) == 7 && | 
|  | strcmp((char*)EVBUFFER_DATA(evb), "hello/1") == 0) | 
|  | test_ok = 1; | 
|  |  | 
|  | evbuffer_free(evb); | 
|  |  | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_evbuffer_find(void) | 
|  | { | 
|  | u_char* p; | 
|  | const char* test1 = "1234567890\r\n"; | 
|  | const char* test2 = "1234567890\r"; | 
|  | #define EVBUFFER_INITIAL_LENGTH 256 | 
|  | char test3[EVBUFFER_INITIAL_LENGTH]; | 
|  | unsigned int i; | 
|  | struct evbuffer * buf = evbuffer_new(); | 
|  |  | 
|  | /* make sure evbuffer_find doesn't match past the end of the buffer */ | 
|  | fprintf(stdout, "Testing evbuffer_find 1: "); | 
|  | evbuffer_add(buf, (u_char*)test1, strlen(test1)); | 
|  | evbuffer_drain(buf, strlen(test1)); | 
|  | evbuffer_add(buf, (u_char*)test2, strlen(test2)); | 
|  | p = evbuffer_find(buf, (u_char*)"\r\n", 2); | 
|  | if (p == NULL) { | 
|  | fprintf(stdout, "OK\n"); | 
|  | } else { | 
|  | fprintf(stdout, "FAILED\n"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * drain the buffer and do another find; in r309 this would | 
|  | * read past the allocated buffer causing a valgrind error. | 
|  | */ | 
|  | fprintf(stdout, "Testing evbuffer_find 2: "); | 
|  | evbuffer_drain(buf, strlen(test2)); | 
|  | for (i = 0; i < EVBUFFER_INITIAL_LENGTH; ++i) | 
|  | test3[i] = 'a'; | 
|  | test3[EVBUFFER_INITIAL_LENGTH - 1] = 'x'; | 
|  | evbuffer_add(buf, (u_char *)test3, EVBUFFER_INITIAL_LENGTH); | 
|  | p = evbuffer_find(buf, (u_char *)"xy", 2); | 
|  | if (p == NULL) { | 
|  | printf("OK\n"); | 
|  | } else { | 
|  | fprintf(stdout, "FAILED\n"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | /* simple test for match at end of allocated buffer */ | 
|  | fprintf(stdout, "Testing evbuffer_find 3: "); | 
|  | p = evbuffer_find(buf, (u_char *)"ax", 2); | 
|  | if (p != NULL && strncmp((char*)p, "ax", 2) == 0) { | 
|  | printf("OK\n"); | 
|  | } else { | 
|  | fprintf(stdout, "FAILED\n"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | evbuffer_free(buf); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * simple bufferevent test | 
|  | */ | 
|  |  | 
|  | static void | 
|  | readcb(struct bufferevent *bev, void *arg) | 
|  | { | 
|  | if (EVBUFFER_LENGTH(bev->input) == 8333) { | 
|  | bufferevent_disable(bev, EV_READ); | 
|  | test_ok++; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | writecb(struct bufferevent *bev, void *arg) | 
|  | { | 
|  | if (EVBUFFER_LENGTH(bev->output) == 0) | 
|  | test_ok++; | 
|  | } | 
|  |  | 
|  | static void | 
|  | errorcb(struct bufferevent *bev, short what, void *arg) | 
|  | { | 
|  | test_ok = -2; | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_bufferevent(void) | 
|  | { | 
|  | struct bufferevent *bev1, *bev2; | 
|  | char buffer[8333]; | 
|  | int i; | 
|  |  | 
|  | setup_test("Bufferevent: "); | 
|  |  | 
|  | bev1 = bufferevent_new(pair[0], readcb, writecb, errorcb, NULL); | 
|  | bev2 = bufferevent_new(pair[1], readcb, writecb, errorcb, NULL); | 
|  |  | 
|  | bufferevent_disable(bev1, EV_READ); | 
|  | bufferevent_enable(bev2, EV_READ); | 
|  |  | 
|  | for (i = 0; i < sizeof(buffer); i++) | 
|  | buffer[i] = i; | 
|  |  | 
|  | bufferevent_write(bev1, buffer, sizeof(buffer)); | 
|  |  | 
|  | event_dispatch(); | 
|  |  | 
|  | bufferevent_free(bev1); | 
|  | bufferevent_free(bev2); | 
|  |  | 
|  | if (test_ok != 2) | 
|  | test_ok = 0; | 
|  |  | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * test watermarks and bufferevent | 
|  | */ | 
|  |  | 
|  | static void | 
|  | wm_readcb(struct bufferevent *bev, void *arg) | 
|  | { | 
|  | int len = EVBUFFER_LENGTH(bev->input); | 
|  | static int nread; | 
|  |  | 
|  | assert(len >= 10 && len <= 20); | 
|  |  | 
|  | evbuffer_drain(bev->input, len); | 
|  |  | 
|  | nread += len; | 
|  | if (nread == 65000) { | 
|  | bufferevent_disable(bev, EV_READ); | 
|  | test_ok++; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | wm_writecb(struct bufferevent *bev, void *arg) | 
|  | { | 
|  | if (EVBUFFER_LENGTH(bev->output) == 0) | 
|  | test_ok++; | 
|  | } | 
|  |  | 
|  | static void | 
|  | wm_errorcb(struct bufferevent *bev, short what, void *arg) | 
|  | { | 
|  | test_ok = -2; | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_bufferevent_watermarks(void) | 
|  | { | 
|  | struct bufferevent *bev1, *bev2; | 
|  | char buffer[65000]; | 
|  | int i; | 
|  |  | 
|  | setup_test("Bufferevent Watermarks: "); | 
|  |  | 
|  | bev1 = bufferevent_new(pair[0], NULL, wm_writecb, wm_errorcb, NULL); | 
|  | bev2 = bufferevent_new(pair[1], wm_readcb, NULL, wm_errorcb, NULL); | 
|  |  | 
|  | bufferevent_disable(bev1, EV_READ); | 
|  | bufferevent_enable(bev2, EV_READ); | 
|  |  | 
|  | for (i = 0; i < sizeof(buffer); i++) | 
|  | buffer[i] = i; | 
|  |  | 
|  | bufferevent_write(bev1, buffer, sizeof(buffer)); | 
|  |  | 
|  | /* limit the reading on the receiving bufferevent */ | 
|  | bufferevent_setwatermark(bev2, EV_READ, 10, 20); | 
|  |  | 
|  | event_dispatch(); | 
|  |  | 
|  | bufferevent_free(bev1); | 
|  | bufferevent_free(bev2); | 
|  |  | 
|  | if (test_ok != 2) | 
|  | test_ok = 0; | 
|  |  | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | struct test_pri_event { | 
|  | struct event ev; | 
|  | int count; | 
|  | }; | 
|  |  | 
|  | static void | 
|  | test_priorities_cb(int fd, short what, void *arg) | 
|  | { | 
|  | struct test_pri_event *pri = arg; | 
|  | struct timeval tv; | 
|  |  | 
|  | if (pri->count == 3) { | 
|  | event_loopexit(NULL); | 
|  | return; | 
|  | } | 
|  |  | 
|  | pri->count++; | 
|  |  | 
|  | evutil_timerclear(&tv); | 
|  | event_add(&pri->ev, &tv); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_priorities(int npriorities) | 
|  | { | 
|  | char buf[32]; | 
|  | struct test_pri_event one, two; | 
|  | struct timeval tv; | 
|  |  | 
|  | evutil_snprintf(buf, sizeof(buf), "Testing Priorities %d: ", npriorities); | 
|  | setup_test(buf); | 
|  |  | 
|  | event_base_priority_init(global_base, npriorities); | 
|  |  | 
|  | memset(&one, 0, sizeof(one)); | 
|  | memset(&two, 0, sizeof(two)); | 
|  |  | 
|  | timeout_set(&one.ev, test_priorities_cb, &one); | 
|  | if (event_priority_set(&one.ev, 0) == -1) { | 
|  | fprintf(stderr, "%s: failed to set priority", __func__); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | timeout_set(&two.ev, test_priorities_cb, &two); | 
|  | if (event_priority_set(&two.ev, npriorities - 1) == -1) { | 
|  | fprintf(stderr, "%s: failed to set priority", __func__); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | evutil_timerclear(&tv); | 
|  |  | 
|  | if (event_add(&one.ev, &tv) == -1) | 
|  | exit(1); | 
|  | if (event_add(&two.ev, &tv) == -1) | 
|  | exit(1); | 
|  |  | 
|  | event_dispatch(); | 
|  |  | 
|  | event_del(&one.ev); | 
|  | event_del(&two.ev); | 
|  |  | 
|  | if (npriorities == 1) { | 
|  | if (one.count == 3 && two.count == 3) | 
|  | test_ok = 1; | 
|  | } else if (npriorities == 2) { | 
|  | /* Two is called once because event_loopexit is priority 1 */ | 
|  | if (one.count == 3 && two.count == 1) | 
|  | test_ok = 1; | 
|  | } else { | 
|  | if (one.count == 3 && two.count == 0) | 
|  | test_ok = 1; | 
|  | } | 
|  |  | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_multiple_cb(int fd, short event, void *arg) | 
|  | { | 
|  | if (event & EV_READ) | 
|  | test_ok |= 1; | 
|  | else if (event & EV_WRITE) | 
|  | test_ok |= 2; | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_multiple_events_for_same_fd(void) | 
|  | { | 
|  | struct event e1, e2; | 
|  |  | 
|  | setup_test("Multiple events for same fd: "); | 
|  |  | 
|  | event_set(&e1, pair[0], EV_READ, test_multiple_cb, NULL); | 
|  | event_add(&e1, NULL); | 
|  | event_set(&e2, pair[0], EV_WRITE, test_multiple_cb, NULL); | 
|  | event_add(&e2, NULL); | 
|  | event_loop(EVLOOP_ONCE); | 
|  | event_del(&e2); | 
|  | write(pair[1], TEST1, strlen(TEST1)+1); | 
|  | event_loop(EVLOOP_ONCE); | 
|  | event_del(&e1); | 
|  |  | 
|  | if (test_ok != 3) | 
|  | test_ok = 0; | 
|  |  | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | int evtag_decode_int(uint32_t *pnumber, struct evbuffer *evbuf); | 
|  | int evtag_encode_tag(struct evbuffer *evbuf, uint32_t number); | 
|  | int evtag_decode_tag(uint32_t *pnumber, struct evbuffer *evbuf); | 
|  |  | 
|  | static void | 
|  | read_once_cb(int fd, short event, void *arg) | 
|  | { | 
|  | char buf[256]; | 
|  | int len; | 
|  |  | 
|  | len = read(fd, buf, sizeof(buf)); | 
|  |  | 
|  | if (called) { | 
|  | test_ok = 0; | 
|  | } else if (len) { | 
|  | /* Assumes global pair[0] can be used for writing */ | 
|  | write(pair[0], TEST1, strlen(TEST1)+1); | 
|  | test_ok = 1; | 
|  | } | 
|  |  | 
|  | called++; | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_want_only_once(void) | 
|  | { | 
|  | struct event ev; | 
|  | struct timeval tv; | 
|  |  | 
|  | /* Very simple read test */ | 
|  | setup_test("Want read only once: "); | 
|  |  | 
|  | write(pair[0], TEST1, strlen(TEST1)+1); | 
|  |  | 
|  | /* Setup the loop termination */ | 
|  | evutil_timerclear(&tv); | 
|  | tv.tv_sec = 1; | 
|  | event_loopexit(&tv); | 
|  |  | 
|  | event_set(&ev, pair[1], EV_READ, read_once_cb, &ev); | 
|  | if (event_add(&ev, NULL) == -1) | 
|  | exit(1); | 
|  | event_dispatch(); | 
|  |  | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  | #define TEST_MAX_INT	6 | 
|  |  | 
|  | static void | 
|  | evtag_int_test(void) | 
|  | { | 
|  | struct evbuffer *tmp = evbuffer_new(); | 
|  | uint32_t integers[TEST_MAX_INT] = { | 
|  | 0xaf0, 0x1000, 0x1, 0xdeadbeef, 0x00, 0xbef000 | 
|  | }; | 
|  | uint32_t integer; | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < TEST_MAX_INT; i++) { | 
|  | int oldlen, newlen; | 
|  | oldlen = EVBUFFER_LENGTH(tmp); | 
|  | encode_int(tmp, integers[i]); | 
|  | newlen = EVBUFFER_LENGTH(tmp); | 
|  | fprintf(stdout, "\t\tencoded 0x%08x with %d bytes\n", | 
|  | integers[i], newlen - oldlen); | 
|  | } | 
|  |  | 
|  | for (i = 0; i < TEST_MAX_INT; i++) { | 
|  | if (evtag_decode_int(&integer, tmp) == -1) { | 
|  | fprintf(stderr, "decode %d failed", i); | 
|  | exit(1); | 
|  | } | 
|  | if (integer != integers[i]) { | 
|  | fprintf(stderr, "got %x, wanted %x", | 
|  | integer, integers[i]); | 
|  | exit(1); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (EVBUFFER_LENGTH(tmp) != 0) { | 
|  | fprintf(stderr, "trailing data"); | 
|  | exit(1); | 
|  | } | 
|  | evbuffer_free(tmp); | 
|  |  | 
|  | fprintf(stdout, "\t%s: OK\n", __func__); | 
|  | } | 
|  |  | 
|  | static void | 
|  | evtag_fuzz(void) | 
|  | { | 
|  | u_char buffer[4096]; | 
|  | struct evbuffer *tmp = evbuffer_new(); | 
|  | struct timeval tv; | 
|  | int i, j; | 
|  |  | 
|  | int not_failed = 0; | 
|  | for (j = 0; j < 100; j++) { | 
|  | for (i = 0; i < sizeof(buffer); i++) | 
|  | buffer[i] = rand(); | 
|  | evbuffer_drain(tmp, -1); | 
|  | evbuffer_add(tmp, buffer, sizeof(buffer)); | 
|  |  | 
|  | if (evtag_unmarshal_timeval(tmp, 0, &tv) != -1) | 
|  | not_failed++; | 
|  | } | 
|  |  | 
|  | /* The majority of decodes should fail */ | 
|  | if (not_failed >= 10) { | 
|  | fprintf(stderr, "evtag_unmarshal should have failed"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | /* Now insert some corruption into the tag length field */ | 
|  | evbuffer_drain(tmp, -1); | 
|  | evutil_timerclear(&tv); | 
|  | tv.tv_sec = 1; | 
|  | evtag_marshal_timeval(tmp, 0, &tv); | 
|  | evbuffer_add(tmp, buffer, sizeof(buffer)); | 
|  |  | 
|  | EVBUFFER_DATA(tmp)[1] = 0xff; | 
|  | if (evtag_unmarshal_timeval(tmp, 0, &tv) != -1) { | 
|  | fprintf(stderr, "evtag_unmarshal_timeval should have failed"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | evbuffer_free(tmp); | 
|  |  | 
|  | fprintf(stdout, "\t%s: OK\n", __func__); | 
|  | } | 
|  |  | 
|  | static void | 
|  | evtag_tag_encoding(void) | 
|  | { | 
|  | struct evbuffer *tmp = evbuffer_new(); | 
|  | uint32_t integers[TEST_MAX_INT] = { | 
|  | 0xaf0, 0x1000, 0x1, 0xdeadbeef, 0x00, 0xbef000 | 
|  | }; | 
|  | uint32_t integer; | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < TEST_MAX_INT; i++) { | 
|  | int oldlen, newlen; | 
|  | oldlen = EVBUFFER_LENGTH(tmp); | 
|  | evtag_encode_tag(tmp, integers[i]); | 
|  | newlen = EVBUFFER_LENGTH(tmp); | 
|  | fprintf(stdout, "\t\tencoded 0x%08x with %d bytes\n", | 
|  | integers[i], newlen - oldlen); | 
|  | } | 
|  |  | 
|  | for (i = 0; i < TEST_MAX_INT; i++) { | 
|  | if (evtag_decode_tag(&integer, tmp) == -1) { | 
|  | fprintf(stderr, "decode %d failed", i); | 
|  | exit(1); | 
|  | } | 
|  | if (integer != integers[i]) { | 
|  | fprintf(stderr, "got %x, wanted %x", | 
|  | integer, integers[i]); | 
|  | exit(1); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (EVBUFFER_LENGTH(tmp) != 0) { | 
|  | fprintf(stderr, "trailing data"); | 
|  | exit(1); | 
|  | } | 
|  | evbuffer_free(tmp); | 
|  |  | 
|  | fprintf(stdout, "\t%s: OK\n", __func__); | 
|  | } | 
|  |  | 
|  | static void | 
|  | evtag_test(void) | 
|  | { | 
|  | fprintf(stdout, "Testing Tagging:\n"); | 
|  |  | 
|  | evtag_init(); | 
|  | evtag_int_test(); | 
|  | evtag_fuzz(); | 
|  |  | 
|  | evtag_tag_encoding(); | 
|  |  | 
|  | fprintf(stdout, "OK\n"); | 
|  | } | 
|  |  | 
|  | #ifndef WIN32 | 
|  | static void | 
|  | rpc_test(void) | 
|  | { | 
|  | struct msg *msg, *msg2; | 
|  | struct kill *attack; | 
|  | struct run *run; | 
|  | struct evbuffer *tmp = evbuffer_new(); | 
|  | struct timeval tv_start, tv_end; | 
|  | uint32_t tag; | 
|  | int i; | 
|  |  | 
|  | fprintf(stdout, "Testing RPC: "); | 
|  |  | 
|  | msg = msg_new(); | 
|  | EVTAG_ASSIGN(msg, from_name, "niels"); | 
|  | EVTAG_ASSIGN(msg, to_name, "phoenix"); | 
|  |  | 
|  | if (EVTAG_GET(msg, attack, &attack) == -1) { | 
|  | fprintf(stderr, "Failed to set kill message.\n"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | EVTAG_ASSIGN(attack, weapon, "feather"); | 
|  | EVTAG_ASSIGN(attack, action, "tickle"); | 
|  |  | 
|  | evutil_gettimeofday(&tv_start, NULL); | 
|  | for (i = 0; i < 1000; ++i) { | 
|  | run = EVTAG_ADD(msg, run); | 
|  | if (run == NULL) { | 
|  | fprintf(stderr, "Failed to add run message.\n"); | 
|  | exit(1); | 
|  | } | 
|  | EVTAG_ASSIGN(run, how, "very fast but with some data in it"); | 
|  | EVTAG_ASSIGN(run, fixed_bytes, | 
|  | (unsigned char*)"012345678901234567890123"); | 
|  | } | 
|  |  | 
|  | if (msg_complete(msg) == -1) { | 
|  | fprintf(stderr, "Failed to make complete message.\n"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | evtag_marshal_msg(tmp, 0xdeaf, msg); | 
|  |  | 
|  | if (evtag_peek(tmp, &tag) == -1) { | 
|  | fprintf(stderr, "Failed to peak tag.\n"); | 
|  | exit (1); | 
|  | } | 
|  |  | 
|  | if (tag != 0xdeaf) { | 
|  | fprintf(stderr, "Got incorrect tag: %0x.\n", tag); | 
|  | exit (1); | 
|  | } | 
|  |  | 
|  | msg2 = msg_new(); | 
|  | if (evtag_unmarshal_msg(tmp, 0xdeaf, msg2) == -1) { | 
|  | fprintf(stderr, "Failed to unmarshal message.\n"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | evutil_gettimeofday(&tv_end, NULL); | 
|  | evutil_timersub(&tv_end, &tv_start, &tv_end); | 
|  | fprintf(stderr, "(%.1f us/add) ", | 
|  | (float)tv_end.tv_sec/(float)i * 1000000.0 + | 
|  | tv_end.tv_usec / (float)i); | 
|  |  | 
|  | if (!EVTAG_HAS(msg2, from_name) || | 
|  | !EVTAG_HAS(msg2, to_name) || | 
|  | !EVTAG_HAS(msg2, attack)) { | 
|  | fprintf(stderr, "Missing data structures.\n"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | if (EVTAG_LEN(msg2, run) != i) { | 
|  | fprintf(stderr, "Wrong number of run messages.\n"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | msg_free(msg); | 
|  | msg_free(msg2); | 
|  |  | 
|  | evbuffer_free(tmp); | 
|  |  | 
|  | fprintf(stdout, "OK\n"); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static void | 
|  | test_evutil_strtoll(void) | 
|  | { | 
|  | const char *s; | 
|  | char *endptr; | 
|  | setup_test("evutil_stroll: "); | 
|  | test_ok = 0; | 
|  |  | 
|  | if (evutil_strtoll("5000000000", NULL, 10) != ((ev_int64_t)5000000)*1000) | 
|  | goto err; | 
|  | if (evutil_strtoll("-5000000000", NULL, 10) != ((ev_int64_t)5000000)*-1000) | 
|  | goto err; | 
|  | s = " 99999stuff"; | 
|  | if (evutil_strtoll(s, &endptr, 10) != (ev_int64_t)99999) | 
|  | goto err; | 
|  | if (endptr != s+6) | 
|  | goto err; | 
|  | if (evutil_strtoll("foo", NULL, 10) != 0) | 
|  | goto err; | 
|  |  | 
|  | test_ok = 1; | 
|  | err: | 
|  | cleanup_test(); | 
|  | } | 
|  |  | 
|  |  | 
|  | int | 
|  | main (int argc, char **argv) | 
|  | { | 
|  | #ifdef WIN32 | 
|  | WORD wVersionRequested; | 
|  | WSADATA wsaData; | 
|  | int	err; | 
|  |  | 
|  | wVersionRequested = MAKEWORD( 2, 2 ); | 
|  |  | 
|  | err = WSAStartup( wVersionRequested, &wsaData ); | 
|  | #endif | 
|  |  | 
|  | #ifndef WIN32 | 
|  | if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) | 
|  | return (1); | 
|  | #endif | 
|  | setvbuf(stdout, NULL, _IONBF, 0); | 
|  |  | 
|  | /* Initalize the event library */ | 
|  | global_base = event_init(); | 
|  |  | 
|  | test_registerfds(); | 
|  |  | 
|  | test_evutil_strtoll(); | 
|  |  | 
|  | /* use the global event base and need to be called first */ | 
|  | test_priorities(1); | 
|  | test_priorities(2); | 
|  | test_priorities(3); | 
|  |  | 
|  | test_evbuffer(); | 
|  | test_evbuffer_find(); | 
|  |  | 
|  | test_bufferevent(); | 
|  | test_bufferevent_watermarks(); | 
|  |  | 
|  | test_free_active_base(); | 
|  |  | 
|  | test_event_base_new(); | 
|  |  | 
|  | http_suite(); | 
|  |  | 
|  | #ifndef WIN32 | 
|  | rpc_suite(); | 
|  | #endif | 
|  |  | 
|  | dns_suite(); | 
|  |  | 
|  | #ifndef WIN32 | 
|  | test_fork(); | 
|  | #endif | 
|  |  | 
|  | test_simpleread(); | 
|  |  | 
|  | test_simplewrite(); | 
|  |  | 
|  | test_multiple(); | 
|  |  | 
|  | test_persistent(); | 
|  |  | 
|  | test_combined(); | 
|  |  | 
|  | test_simpletimeout(); | 
|  | #ifndef WIN32 | 
|  | test_simplesignal(); | 
|  | test_multiplesignal(); | 
|  | test_immediatesignal(); | 
|  | #endif | 
|  | test_loopexit(); | 
|  | test_loopbreak(); | 
|  |  | 
|  | test_loopexit_multiple(); | 
|  |  | 
|  | test_multiple_events_for_same_fd(); | 
|  |  | 
|  | test_want_only_once(); | 
|  |  | 
|  | evtag_test(); | 
|  |  | 
|  | #ifndef WIN32 | 
|  | rpc_test(); | 
|  |  | 
|  | test_signal_dealloc(); | 
|  | test_signal_pipeloss(); | 
|  | test_signal_switchbase(); | 
|  | test_signal_restore(); | 
|  | test_signal_assert(); | 
|  | test_signal_while_processing(); | 
|  | #endif | 
|  |  | 
|  | return (0); | 
|  | } | 
|  |  |