The big win here is that we can get process-level isolation.
This has been tested to work okay on at least Linux and Win32. Only
the tests in regress.c have been converted wrapped in the new wrapper
functions; the others are still on the old system.
svn:r1073
AUTOMAKE_OPTIONS = foreign no-dependencies
-AM_CFLAGS = -I$(top_srcdir) -I$(top_srcdir)/compat -I$(top_srcdir)/include
+AM_CFLAGS = -I$(top_srcdir) -I$(top_srcdir)/compat -I$(top_srcdir)/include
EXTRA_DIST = regress.rpc regress.gen.h regress.gen.c
noinst_PROGRAMS = test-init test-eof test-weof test-time regress \
bench bench_cascade bench_http
+noinst_HEADERS = tinytest.h tinytest_macros.h regress.h
BUILT_SOURCES = regress.gen.c regress.gen.h
test_init_SOURCES = test-init.c
test_weof_LDADD = ../libevent_core.la
test_time_SOURCES = test-time.c
test_time_LDADD = ../libevent_core.la
-regress_SOURCES = regress.c regress.h regress_http.c regress_dns.c \
+
+regress_SOURCES = regress.c regress_http.c regress_dns.c \
regress_rpc.c regress.gen.c regress.gen.h regress_et.c \
- regress_util.c \
+ regress_util.c tinytest.c regress_main.c \
$(regress_pthread_SOURCES) $(regress_zlib_SOURCES)
if PTHREADS
regress_pthread_SOURCES = regress_pthread.c
regress_CFLAGS = -I$(top_srcdir) -I$(top_srcdir)/compat \
-I$(top_srcdir)/include $(PTHREAD_CFLAGS) $(ZLIB_CFLAGS)
regress_LDFLAGS = $(PTHREAD_CFLAGS)
+
bench_SOURCES = bench.c
bench_LDADD = ../libevent.la
bench_cascade_SOURCES = bench_cascade.c
int pair[2];
int test_ok;
-static int called;
+int called;
+struct event_base *global_base;
+
static char wbuf[4096];
static char rbuf[4096];
static int woff;
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
static int
setup_test(const char *name)
{
+ if (in_legacy_test_wrapper)
+ return 0;
fprintf(stdout, "%s", name);
static int
cleanup_test(void)
{
+ if (in_legacy_test_wrapper)
+ return 0;
+
#ifndef WIN32
close(pair[0]);
close(pair[1]);
struct event ev;
int count = 0;
- setup_test("Periodic timeout via EV_PERSIST: ");
-
timerclear(&tv);
tv.tv_usec = 10000;
event_del(&ev);
- cleanup_test();
}
#ifndef WIN32
cleanup_test();
}
-/* validates that an evbuffer is good */
+/* Validates that an evbuffer is good. */
static void
evbuffer_validate(struct evbuffer *buf)
{
}
static void
-test_evbuffer_find(void)
+test_evbuffer_find(void *ptr)
{
u_char* p;
const char* test1 = "1234567890\r\n";
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_validate(buf);
evbuffer_drain(buf, strlen(test1));
evbuffer_add(buf, (u_char*)test2, strlen(test2));
evbuffer_validate(buf);
p = evbuffer_find(buf, (u_char*)"\r\n", 2);
- if (p == NULL) {
- fprintf(stdout, "OK\n");
- } else {
- fprintf(stdout, "FAILED\n");
- exit(1);
- }
+ tt_want(p == NULL);
/*
* 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));
evbuffer_validate(buf);
for (i = 0; i < EVBUFFER_INITIAL_LENGTH; ++i)
evbuffer_add(buf, (u_char *)test3, EVBUFFER_INITIAL_LENGTH);
evbuffer_validate(buf);
p = evbuffer_find(buf, (u_char *)"xy", 2);
- if (p == NULL) {
- printf("OK\n");
- } else {
- fprintf(stdout, "FAILED\n");
- exit(1);
- }
+ tt_want(p == NULL);
/* 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);
- }
+ tt_assert(p != NULL);
+ tt_want(strncmp((char*)p, "ax", 2) == 0);
- evbuffer_free(buf);
+end:
+ if (buf)
+ evbuffer_free(buf);
}
/*
}
static void
-test_priorities(int npriorities)
+test_priorities_impl(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);
+ TT_BLATHER(("Testing Priorities %d: ", npriorities));
event_base_priority_init(global_base, npriorities);
if (one.count == 3 && two.count == 0)
test_ok = 1;
}
+}
- cleanup_test();
+static void
+test_priorities(void)
+{
+ test_priorities_impl(1);
+ if (test_ok)
+ test_priorities_impl(2);
+ if (test_ok)
+ test_priorities_impl(3);
}
+
static void
test_multiple_cb(int fd, short event, void *arg)
{
{
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;
test_ok = 1;
- err:
- cleanup_test();
+err:
+ ;
}
static void
-test_evutil_snprintf(void)
+test_evutil_snprintf(void *ptr)
{
char buf[16];
int r;
- setup_test("evutil_snprintf: ");
- test_ok = 0;
r = evutil_snprintf(buf, sizeof(buf), "%d %d", 50, 100);
- if (strcmp(buf, "50 100")) {
- fprintf(stderr, "buf='%s'\n", buf);
- goto err;
- }
- if (r != 6) {
- fprintf(stderr, "r=%d\n", r);
- goto err;
- }
+ tt_str_op(buf, ==, "50 100");
+ tt_int_op(r, ==, 6);
r = evutil_snprintf(buf, sizeof(buf), "longish %d", 1234567890);
- if (strcmp(buf, "longish 1234567")) {
- fprintf(stderr, "buf='%s'\n", buf);
- goto err;
- }
- if (r != 18) {
- fprintf(stderr, "r=%d\n", r);
- goto err;
- }
+ tt_str_op(buf, ==, "longish 1234567");
+ tt_int_op(r, ==, 18);
- test_ok = 1;
- err:
- cleanup_test();
+ end:
+ ;
}
static void
-test_methods(void)
+test_methods(void *ptr)
{
const char **methods = event_get_supported_methods();
- struct event_config *cfg;
- struct event_base *base;
+ struct event_config *cfg = NULL;
+ struct event_base *base = NULL;
const char *backend;
int n_methods = 0;
- fprintf(stdout, "Testing supported methods: ");
-
- if (methods == NULL) {
- fprintf(stdout, "FAILED\n");
- exit(1);
- }
+ tt_assert(methods);
backend = methods[0];
while (*methods != NULL) {
- fprintf(stdout, "%s ", *methods);
+ TT_BLATHER(("Support method: %s", *methods));
++methods;
++n_methods;
}
if (n_methods == 1) {
/* only one method supported; can't continue. */
- goto done;
+ goto end;
}
cfg = event_config_new();
assert(cfg != NULL);
- assert(event_config_avoid_method(cfg, backend) == 0);
+ tt_int_op(event_config_avoid_method(cfg, backend), ==, 0);
base = event_base_new_with_config(cfg);
- if (base == NULL) {
- fprintf(stdout, "FAILED\n");
- exit(1);
- }
-
- if (strcmp(backend, event_base_get_method(base)) == 0) {
- fprintf(stdout, "FAILED\n");
- exit(1);
- }
-
- event_base_free(base);
- event_config_free(cfg);
-done:
- fprintf(stdout, "OK\n");
-}
-
-
-int
-main (int argc, char **argv)
-{
-#ifdef WIN32
- WORD wVersionRequested;
- WSADATA wsaData;
- int err;
-
- wVersionRequested = MAKEWORD( 2, 2 );
+ tt_assert(base);
+
+ tt_str_op(backend, !=, event_base_get_method(base));
+
+end:
+ if (base)
+ event_base_free(base);
+ if (cfg)
+ event_config_free(cfg);
+}
+
+/* All the flags we set */
+#define TT_ISOLATED TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_BASE
+
+struct testcase_t legacy_testcases[] = {
+ /* Some converted-over tests */
+ { "methods", test_methods, TT_FORK, NULL, NULL },
+ { "evutil_snprintf", test_evutil_snprintf, 0, NULL, NULL },
+
+ /* These are still using the old API */
+ LEGACY(evutil_strtoll, 0),
+ LEGACY(persistent_timeout, TT_FORK|TT_NEED_BASE),
+ LEGACY(priorities, TT_FORK|TT_NEED_BASE),
+
+ /* These need to fork because of evbuffer_validate. Otherwise
+ * they'd be fine in the main process, since they don't mess
+ * with global state. */
+ LEGACY(evbuffer, TT_FORK),
+ LEGACY(evbuffer_reference, TT_FORK),
+ LEGACY(evbuffer_iterative, TT_FORK),
+ LEGACY(evbuffer_readln, TT_FORK),
+ { "evbuffer_find", test_evbuffer_find, TT_FORK, NULL, NULL },
+
+ LEGACY(bufferevent, TT_ISOLATED),
+ LEGACY(bufferevent_watermarks, TT_ISOLATED),
+ LEGACY(bufferevent_filters, TT_ISOLATED),
+
+ LEGACY(free_active_base, TT_FORK|TT_NEED_BASE),
+ LEGACY(event_base_new, TT_FORK|TT_NEED_SOCKETPAIR),
+
+ /* These legacy tests may not all need all of these flags. */
+ LEGACY(simpleread, TT_ISOLATED),
+ LEGACY(simpleread_multiple, TT_ISOLATED),
+ LEGACY(simplewrite, TT_ISOLATED),
+ LEGACY(multiple, TT_ISOLATED),
+ LEGACY(persistent, TT_ISOLATED),
+ LEGACY(combined, TT_ISOLATED),
+ LEGACY(simpletimeout, TT_ISOLATED),
+ LEGACY(loopbreak, TT_ISOLATED),
+ LEGACY(loopexit, TT_ISOLATED),
+ LEGACY(loopexit_multiple, TT_ISOLATED),
+ LEGACY(nonpersist_readd, TT_ISOLATED),
+ LEGACY(multiple_events_for_same_fd, TT_ISOLATED),
+ LEGACY(want_only_once, TT_ISOLATED),
- err = WSAStartup( wVersionRequested, &wsaData );
+#ifndef WIN32
+ LEGACY(fork, TT_ISOLATED),
#endif
+ END_OF_TESTCASES
+};
+
+struct testcase_t signal_testcases[] = {
#ifndef WIN32
- if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
- return (1);
+ LEGACY(simplesignal, TT_ISOLATED),
+ LEGACY(multiplesignal, TT_ISOLATED),
+ LEGACY(immediatesignal, TT_ISOLATED),
+ LEGACY(signal_dealloc, TT_ISOLATED),
+ LEGACY(signal_pipeloss, TT_ISOLATED),
+ LEGACY(signal_switchbase, TT_ISOLATED),
+ LEGACY(signal_restore, TT_ISOLATED),
+ LEGACY(signal_assert, TT_ISOLATED),
+ LEGACY(signal_while_processing, TT_ISOLATED),
#endif
- setvbuf(stdout, NULL, _IONBF, 0);
-
- test_methods();
+ END_OF_TESTCASES
+};
+int
+legacy_main(void)
+{
/* Initalize the event library */
global_base = event_init();
- test_evutil_strtoll();
- test_evutil_snprintf();
- util_suite();
-
- test_persistent_timeout();
-
- /* use the global event base and need to be called first */
- test_priorities(1);
- test_priorities(2);
- test_priorities(3);
-
- test_evbuffer();
- test_evbuffer_reference();
- test_evbuffer_iterative();
- test_evbuffer_readln();
- test_evbuffer_find();
-
- test_bufferevent();
- test_bufferevent_watermarks();
- test_bufferevent_filters();
-
- test_free_active_base();
-
- test_event_base_new();
-
#if defined(_EVENT_HAVE_PTHREADS) && !defined(_EVENT_DISABLE_THREAD_SUPPORT)
regress_pthread();
#endif
regress_zlib();
#endif
- test_simpleread();
- test_simpleread_multiple();
-
- test_simplewrite();
-
- test_multiple();
-
- test_persistent();
-
- test_combined();
-
- test_simpletimeout();
-
-#ifndef WIN32
- test_fork();
-#endif
-
-#ifndef WIN32
- test_edgetriggered();
- test_simplesignal();
- test_multiplesignal();
- test_immediatesignal();
-#endif
- test_loopexit();
- test_loopbreak();
-
http_suite();
#ifndef WIN32
dns_suite();
- test_loopexit_multiple();
-
- test_nonpersist_readd();
-
- test_multiple_events_for_same_fd();
-
- test_want_only_once();
-
evtag_test();
rpc_test();
-#ifndef WIN32
- test_signal_dealloc();
- test_signal_pipeloss();
- test_signal_switchbase();
- test_signal_restore();
- test_signal_assert();
- test_signal_while_processing();
-#endif
-
return (0);
}
extern "C" {
#endif
+#include "tinytest.h"
+#include "tinytest_macros.h"
+
+extern struct testcase_t legacy_testcases[];
+extern struct testcase_t util_testcases[];
+extern struct testcase_t signal_testcases[];
+
+int legacy_main(void);
+
void http_suite(void);
void http_basic_test(void);
void dns_suite(void);
-void util_suite(void);
-
void regress_pthread(void);
void regress_zlib(void);
void test_edgetriggered(void);
+/* Helpers to wrap old testcases */
+extern int pair[2];
+extern int test_ok;
+extern int called;
+extern struct event_base *global_base;
+extern int in_legacy_test_wrapper;
+
+extern const struct testcase_setup_t legacy_setup;
+void run_legacy_test_fn(void *ptr);
+
+/* A couple of flags that legacy_setup can support. */
+#define TT_NEED_SOCKETPAIR TT_FIRST_USER_FLAG
+#define TT_NEED_BASE (TT_FIRST_USER_FLAG<<1)
+
+#define LEGACY(name,flags) \
+ { #name, run_legacy_test_fn, flags, &legacy_setup, \
+ test_## name }
+
+
#ifdef __cplusplus
}
#endif
#include "regress.h"
-static int called = 0;
static int was_et = 0;
static void
--- /dev/null
+/*
+ * Copyright (c) 2003-2007 Niels Provos <provos@citi.umich.edu>
+ * Copyright (c) 2007-2009 Niels Provos and Nick Mathewson
+ *
+ * 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 "event-config.h"
+#endif
+
+#if 0
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef _EVENT_HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <sys/queue.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#endif
+
+
+#ifndef WIN32
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <unistd.h>
+#include <netdb.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include <event2/util.h>
+#include <event2/event.h>
+#include <event2/event_compat.h>
+
+#include "regress.h"
+#include "tinytest.h"
+#include "tinytest_macros.h"
+
+/* ============================================================ */
+/* Code to wrap up old legacy test cases that used setup() and cleanup(). */
+
+/* This is set to true if we're inside a legacy test wrapper. It lets the
+ setup() and cleanup() functions in regress.c know they're not needed.
+ */
+int in_legacy_test_wrapper = 0;
+
+/* The "data" for a legacy test is just a pointer to the void fn(void)
+ function implementing the test case. We need to set up some globals,
+ though, since that's where legacy tests expect to find a socketpair
+ (sometimes) and a global event_base (sometimes).
+ */
+static void *
+legacy_test_setup(const struct testcase_t *testcase)
+{
+ if (testcase->flags & TT_NEED_SOCKETPAIR) {
+ if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
+ fprintf(stderr, "%s: socketpair\n", __func__);
+ exit(1);
+ }
+
+ if (evutil_make_socket_nonblocking(pair[0]) == -1) {
+ fprintf(stderr, "fcntl(O_NONBLOCK)");
+ exit(1);
+ }
+
+ if (evutil_make_socket_nonblocking(pair[1]) == -1) {
+ fprintf(stderr, "fcntl(O_NONBLOCK)");
+ exit(1);
+ }
+ }
+ if (testcase->flags & TT_NEED_BASE) {
+ global_base = event_init();
+ }
+
+ return testcase->setup_data;
+}
+
+/* This function is the implementation of every legacy test case. It
+ sets test_ok to 0, invokes the test function, and tells tinytest that
+ the test failed if the test didn't set test_ok to 1.
+ */
+void
+run_legacy_test_fn(void *ptr)
+{
+ void (*fn)(void);
+ test_ok = called = 0;
+ fn = ptr;
+
+ in_legacy_test_wrapper = 1;
+ fn(); /* This part actually calls the test */
+ in_legacy_test_wrapper = 0;
+
+ if (!test_ok)
+ tt_abort_msg("Legacy unit test failed");
+
+end:
+ test_ok = 0;
+}
+
+/* This function doesn't have to clean up ptr (which is just a pointer
+ to the test function), but it may need to close the socketpair or
+ free the event_base.
+ */
+static int
+legacy_test_cleanup(const struct testcase_t *testcase, void *ptr)
+{
+ (void)ptr;
+ if (testcase->flags & TT_NEED_SOCKETPAIR) {
+ if (pair[0] != -1)
+ EVUTIL_CLOSESOCKET(pair[0]);
+ if (pair[1] != -1)
+ EVUTIL_CLOSESOCKET(pair[1]);
+ pair[0] = pair[1] = -1;
+ }
+
+ if (testcase->flags & TT_NEED_BASE) {
+ event_base_free(global_base);
+ global_base = NULL;
+ }
+
+
+ return 1;
+}
+
+const struct testcase_setup_t legacy_setup = {
+ legacy_test_setup, legacy_test_cleanup
+};
+
+/* ============================================================ */
+
+
+struct testgroup_t testgroups[] = {
+ { "main/", legacy_testcases },
+ { "signal/", signal_testcases },
+ { "util/", util_testcases },
+ END_OF_GROUPS
+};
+
+int
+main(int argc, const 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
+
+ if (tinytest_main(argc,argv,testgroups))
+ return 1;
+
+ in_legacy_test_wrapper = 0;
+ return legacy_main();
+}
+
#include "event2/util.h"
#include "../ipv6-internal.h"
-void util_suite(void);
+#include "regress.h"
enum entry_status { NORMAL, CANONICAL, BAD };
};
static void
-regress_ipv4_parse(void)
+regress_ipv4_parse(void *ptr)
{
int i;
- int ok = 1;
- printf("Testing IPv4 parsing...");
-
for (i = 0; ipv4_entries[i].addr; ++i) {
char written[128];
struct ipv4_entry *ent = &ipv4_entries[i];
r = evutil_inet_pton(AF_INET, ent->addr, &in);
if (r == 0) {
if (ent->status != BAD) {
- printf("%s did not parse, but it's a good address!\n",
- ent->addr);
- ok = 0;
+ TT_FAIL(("%s did not parse, but it's a good address!",
+ ent->addr));
}
continue;
}
if (ent->status == BAD) {
- printf("%s parsed, but we expected an error\n", ent->addr);
- ok = 0;
+ TT_FAIL(("%s parsed, but we expected an error", ent->addr));
continue;
}
if (ntohl(in.s_addr) != ent->res) {
- printf("%s parsed to %lx, but we expected %lx\n", ent->addr,
- (unsigned long)ntohl(in.s_addr),
- (unsigned long)ent->res);
- ok = 0;
+ TT_FAIL(("%s parsed to %lx, but we expected %lx", ent->addr,
+ (unsigned long)ntohl(in.s_addr),
+ (unsigned long)ent->res));
continue;
}
if (ent->status == CANONICAL) {
const char *w = evutil_inet_ntop(AF_INET, &in, written,
sizeof(written));
if (!w) {
- printf("Tried to write out %s; got NULL.\n", ent->addr);
- ok = 0;
+ TT_FAIL(("Tried to write out %s; got NULL.", ent->addr));
continue;
}
if (strcmp(written, ent->addr)) {
- printf("Tried to write out %s; got %s\n", ent->addr, written);
- ok = 0;
+ TT_FAIL(("Tried to write out %s; got %s",
+ ent->addr, written));
continue;
}
}
}
- if (!ok) {
- printf("FAILED\n");
- exit(1);
- }
- printf("OK\n");
+
}
static void
-regress_ipv6_parse(void)
+regress_ipv6_parse(void *ptr)
{
#ifdef AF_INET6
int i, j;
- int ok = 1;
- printf("Testing IPv6 parsing...");
for (i = 0; ipv6_entries[i].addr; ++i) {
char written[128];
int r;
r = evutil_inet_pton(AF_INET6, ent->addr, &in6);
if (r == 0) {
- if (ent->status != BAD) {
- printf("%s did not parse, but it's a good address!\n",
- ent->addr);
- ok = 0;
- }
+ if (ent->status != BAD)
+ TT_FAIL(("%s did not parse, but it's a good address!",
+ ent->addr));
continue;
}
if (ent->status == BAD) {
- printf("%s parsed, but we expected an error\n", ent->addr);
- ok = 0;
+ TT_FAIL(("%s parsed, but we expected an error", ent->addr));
continue;
}
for (j = 0; j < 4; ++j) {
(in6.s6_addr[j*4+2] << 8) |
(in6.s6_addr[j*4+3]);
if (u != ent->res[j]) {
- printf("%s did not parse as expected.\n", ent->addr);
- ok = 0;
+ TT_FAIL(("%s did not parse as expected.", ent->addr));
continue;
}
}
const char *w = evutil_inet_ntop(AF_INET6, &in6, written,
sizeof(written));
if (!w) {
- printf("Tried to write out %s; got NULL.\n", ent->addr);
- ok = 0;
+ TT_FAIL(("Tried to write out %s; got NULL.", ent->addr));
continue;
}
if (strcmp(written, ent->addr)) {
- printf("Tried to write out %s; got %s\n", ent->addr, written);
- ok = 0;
+ TT_FAIL(("Tried to write out %s; got %s", ent->addr, written));
continue;
}
}
}
- if (!ok) {
- printf("FAILED\n");
- exit(1);
- }
- printf("OK\n");
#else
- print("Skipping IPv6 address parsing.\n");
+ TT_BLATHER(("Skipping IPv6 address parsing."));
#endif
}
};
static void
-regress_sockaddr_port_parse(void)
+regress_sockaddr_port_parse(void *ptr)
{
struct sockaddr_storage ss;
- int ok = 1;
int i, r;
for (i = 0; sa_port_ents[i].parse; ++i) {
memset(&ss, 0, sizeof(ss));
r = evutil_parse_sockaddr_port(ent->parse, (struct sockaddr*)&ss, sizeof(ss));
if (r < 0) {
- if (ent->sa_family) {
- printf("Couldn't parse %s!\n", ent->parse);
- ok = 0;
- }
+ if (ent->sa_family)
+ TT_FAIL(("Couldn't parse %s!", ent->parse));
continue;
} else if (! ent->sa_family) {
- printf("Shouldn't have been able to parse %s!\n",
- ent->parse);
- ok = 0;
+ TT_FAIL(("Shouldn't have been able to parse %s!", ent->parse));
continue;
}
if (ent->sa_family == AF_INET) {
sin.sin_port = htons(ent->port);
r = evutil_inet_pton(AF_INET, ent->addr, &sin.sin_addr);
if (1 != r) {
- printf("Couldn't parse ipv4 target %s.\n", ent->addr);
- ok = 0;
+ TT_FAIL(("Couldn't parse ipv4 target %s.", ent->addr));
} else if (memcmp(&sin, &ss, sizeof(sin))) {
- printf("Parse for %s was not as expected.\n", ent->parse);
- ok = 0;
+ TT_FAIL(("Parse for %s was not as expected.", ent->parse));
}
} else {
struct sockaddr_in6 sin6;
sin6.sin6_port = htons(ent->port);
r = evutil_inet_pton(AF_INET6, ent->addr, &sin6.sin6_addr);
if (1 != r) {
- printf("Couldn't parse ipv6 target %s.\n", ent->addr);
- ok = 0;
+ TT_FAIL(("Couldn't parse ipv6 target %s.", ent->addr));
} else if (memcmp(&sin6, &ss, sizeof(sin6))) {
- printf("Parse for %s was not as expected.\n", ent->parse);
- ok = 0;
+ TT_FAIL(("Parse for %s was not as expected.", ent->parse));
}
}
}
-
- if (!ok) {
- printf("FAILED\n");
- exit(1);
- }
- printf("OK\n");
}
-void
-util_suite(void)
-{
- regress_ipv4_parse();
- regress_ipv6_parse();
- regress_sockaddr_port_parse();
-}
+struct testcase_t util_testcases[] = {
+ { "ipv4_parse", regress_ipv4_parse, 0, NULL, NULL },
+ { "ipv6_parse", regress_ipv6_parse, 0, NULL, NULL },
+ { "sockaddr_port_parse", regress_sockaddr_port_parse, 0, NULL, NULL },
+ END_OF_TESTCASES,
+};
+
+
--- /dev/null
+/* tinytest.c -- Copyright 2009 Nick Mathewson
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#ifdef WIN32
+#include <windows.h>
+#else
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#endif
+
+#include "tinytest.h"
+#include "tinytest_macros.h"
+
+#define LONGEST_TEST_NAME 16384
+
+static int in_tinytest_main = 0; /**< true if we're in tinytest_main().*/
+static int n_ok = 0; /**< Number of tests that have passed */
+static int n_bad = 0; /**< Number of tests that have failed. */
+
+static int opt_forked = 0; /**< True iff we're called from inside a win32 fork*/
+static int opt_verbosity = 1; /**< 0==quiet,1==normal,2==verbose */
+
+static int cur_test_outcome = 0; /**< True iff the current test has failed. */
+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;
+
+#ifdef WIN32
+/** Pointer to argv[0] for win32. */
+static const char *commandname = NULL;
+#endif
+
+static int
+_testcase_run_bare(const struct testcase_t *testcase)
+{
+ void *env = NULL;
+ int outcome;
+ if (testcase->setup) {
+ env = testcase->setup->setup_fn(testcase);
+ assert(env);
+ }
+
+ cur_test_outcome = 1;
+ testcase->fn(env);
+ outcome = cur_test_outcome;
+
+ if (testcase->setup) {
+ if (testcase->setup->cleanup_fn(testcase, env) == 0)
+ outcome = 0;
+ }
+
+ return outcome;
+}
+
+static int
+_testcase_run_forked(const struct testgroup_t *group,
+ const struct testcase_t *testcase)
+{
+#ifdef WIN32
+ /* Fork? On Win32? How primitive! We'll do what the smart kids do:
+ we'll invoke our own exe (whose name we recall from the command
+ line) with a command line that tells it to run just the test we
+ want, and this time without forking.
+
+ (No, threads aren't an option. The whole point of forking is to
+ share no state between tests.)
+ */
+ int ok;
+ char buffer[LONGEST_TEST_NAME+256];
+ const char *verbosity;
+ STARTUPINFO si;
+ PROCESS_INFORMATION info;
+ DWORD exitcode;
+
+ if (!in_tinytest_main) {
+ printf("\nERROR. On Windows, _testcase_run_forked must be"
+ " called from within tinytest_main.\n");
+ abort();
+ }
+ if (opt_verbosity)
+ printf("[forking] ");
+
+ verbosity = (opt_verbosity == 2) ? "--verbose" :
+ (opt_verbosity == 0) ? "--quiet" : "";
+ snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s %s%s",
+ commandname, verbosity, group->prefix, testcase->name);
+
+ memset(&si, 0, sizeof(si));
+ memset(&info, 0, sizeof(info));
+ si.cb = sizeof(si);
+
+ ok = CreateProcess(commandname, buffer, NULL, NULL, 0,
+ 0, NULL, NULL, &si, &info);
+ if (!ok) {
+ printf("CreateProcess failed!\n");
+ return 0;
+ }
+ WaitForSingleObject(info.hProcess, INFINITE);
+ GetExitCodeProcess(info.hProcess, &exitcode);
+ CloseHandle(info.hProcess);
+ CloseHandle(info.hThread);
+ return exitcode == 0;
+#else
+ int outcome_pipe[2];
+ pid_t pid;
+ (void)group;
+
+ if (pipe(outcome_pipe))
+ perror("opening pipe");
+
+ if (opt_verbosity)
+ printf("[forking] ");
+ pid = fork();
+ if (!pid) {
+ /* child. */
+ int test_r, write_r;
+ close(outcome_pipe[0]);
+ test_r = _testcase_run_bare(testcase);
+ write_r = write(outcome_pipe[1], test_r ? "Y" : "N", 1);
+ if (write_r != 1) {
+ perror("write outcome to pipe");
+ exit(1);
+ }
+ exit(0);
+ } else {
+ /* parent */
+ int status, r;
+ char b[1];
+ /* Close this now, so that if the other side closes it,
+ * our read fails. */
+ close(outcome_pipe[1]);
+ r = read(outcome_pipe[0], b, 1);
+ if (r == 0) {
+ printf("[Lost connection!] ");
+ return 0;
+ } else if (r != 1) {
+ perror("read outcome from pipe");
+ }
+ waitpid(pid, &status, 0);
+ close(outcome_pipe[0]);
+ return b[0] == 'Y' ? 1 : 0;
+ }
+#endif
+}
+
+int
+testcase_run_one(const struct testgroup_t *group,
+ const struct testcase_t *testcase)
+{
+ int outcome;
+
+ if (testcase->flags & TT_SKIP) {
+ if (opt_verbosity)
+ printf("%s%s... SKIPPED\n",
+ group->prefix, testcase->name);
+ return 1;
+ }
+
+ if (opt_verbosity && !opt_forked)
+ printf("%s%s... ", group->prefix, testcase->name);
+ else {
+ cur_test_prefix = group->prefix;
+ cur_test_name = testcase->name;
+ }
+
+ if ((testcase->flags & TT_FORK) && !opt_forked) {
+ outcome = _testcase_run_forked(group, testcase);
+ } else {
+ outcome = _testcase_run_bare(testcase);
+ }
+
+ if (outcome) {
+ ++n_ok;
+ if (opt_verbosity && !opt_forked)
+ puts(opt_verbosity==1?"OK":"");
+ } else {
+ ++n_bad;
+ if (!opt_forked)
+ printf("\n [%s FAILED]\n", testcase->name);
+ }
+
+ if (opt_forked) {
+ exit(outcome ? 0 : 1);
+ } else {
+ return outcome;
+ }
+}
+
+int
+_tinytest_set_flag(struct testgroup_t *groups, const char *arg, unsigned long flag)
+{
+ int i, j;
+ int length = LONGEST_TEST_NAME;
+ char fullname[LONGEST_TEST_NAME];
+ int found=0;
+ if (strstr(arg, ".."))
+ length = strstr(arg,"..")-arg;
+ for (i=0; groups[i].prefix; ++i) {
+ for (j=0; groups[i].cases[j].name; ++j) {
+ snprintf(fullname, sizeof(fullname), "%s%s",
+ groups[i].prefix, groups[i].cases[j].name);
+ if (!flag) /* Hack! */
+ printf(" %s\n", fullname);
+ if (!strncmp(fullname, arg, length)) {
+ groups[i].cases[j].flags |= flag;
+ ++found;
+ }
+ }
+ }
+ return found;
+}
+
+static void
+usage(struct testgroup_t *groups)
+{
+ puts("Options are: --verbose --quiet");
+ puts("Known tests are:");
+ _tinytest_set_flag(groups, "..", 0);
+ exit(0);
+}
+
+int
+tinytest_main(int c, const char **v, struct testgroup_t *groups)
+{
+ int i, j, n=0;
+
+#ifdef WIN32
+ commandname = v[0];
+#endif
+ for (i=1; i<c; ++i) {
+ if (v[i][0] == '-') {
+ if (!strcmp(v[i], "--RUNNING-FORKED"))
+ opt_forked = 1;
+ else if (!strcmp(v[i], "--quiet"))
+ opt_verbosity = 0;
+ else if (!strcmp(v[i], "--verbose"))
+ opt_verbosity = 2;
+ else if (!strcmp(v[i], "--help"))
+ usage(groups);
+ else {
+ printf("Unknown option %s. Try --help\n",v[i]);
+ return -1;
+ }
+ } else {
+ ++n;
+ if (!_tinytest_set_flag(groups, v[i], _TT_ENABLED)) {
+ printf("No such test as %s!\n", v[i]);
+ return -1;
+ }
+ }
+ }
+ if (!n)
+ _tinytest_set_flag(groups, "...", _TT_ENABLED);
+
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ ++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]);
+
+ --in_tinytest_main;
+
+ if (n_bad)
+ printf("%d TESTS FAILED.\n", n_bad);
+ return (n_bad == 0) ? 0 : 1;
+}
+
+int
+_tinytest_get_verbosity(void)
+{
+ return opt_verbosity;
+}
+
+void
+_tinytest_set_test_failed(void)
+{
+ if (opt_verbosity == 0 && cur_test_name) {
+ printf("%s%s... ", cur_test_prefix, cur_test_name);
+ cur_test_name = NULL;
+ }
+ cur_test_outcome = 0;
+}
+
--- /dev/null
+/* tinytest.h -- Copyright 2009 Nick Mathewson
+ *
+ * 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.
+ */
+
+#ifndef _TINYTEST_H
+#define _TINYTEST_H
+
+/** Flag for a test that needs to run in a subprocess. */
+#define TT_FORK (1<<0)
+/** Runtime flag for a test we've decided to skip. */
+#define TT_SKIP (1<<1)
+/** Internal runtime flag for a test we've decided to run. */
+#define _TT_ENABLED (1<<2)
+/** If you add your own flags, make them start at this point. */
+#define TT_FIRST_USER_FLAG (1<<3)
+
+typedef void (*testcase_fn)(void *);
+
+struct testcase_t;
+
+/** Functions to initialize/teardown a structure for a testcase. */
+struct testcase_setup_t {
+ /** Return a new structure for use by a given testcase. */
+ void *(*setup_fn)(const struct testcase_t *);
+ /** Clean/free a structure from setup_fn. Return 1 if ok, 0 on err. */
+ int (*cleanup_fn)(const struct testcase_t *, void *);
+};
+
+/** A single test-case that you can run. */
+struct testcase_t {
+ const char *name; /**< An identifier for this case. */
+ testcase_fn fn; /**< The function to run to implement this case. */
+ unsigned long flags; /**< Bitfield of TT_* flags. */
+ const struct testcase_setup_t *setup; /**< Optional setup/cleanup fns*/
+ void *setup_data; /**< Extra data usable by setup function */
+};
+#define END_OF_TESTCASES { NULL, NULL, 0, NULL, NULL }
+
+/** A group of tests that are selectable together. */
+struct testgroup_t {
+ const char *prefix; /**< Prefix to prepend to testnames. */
+ struct testcase_t *cases; /** Array, ending with END_OF_TESTCASES */
+};
+#define END_OF_GROUPS { NULL, NULL}
+
+/** Implementation: called from a test to indicate failure, before logging. */
+void _tinytest_set_test_failed(void);
+/** Implementation: return 0 for quiet, 1 for normal, 2 for loud. */
+int _tinytest_get_verbosity(void);
+/** Implementation: Set a flag on tests matching a name; returns number
+ * of tests that matched. */
+int _tinytest_set_flag(struct testgroup_t *, const char *, unsigned long);
+
+/** Set all tests in 'groups' matching the name 'named' to be skipped. */
+#define tinytest_skip(groups, named) \
+ _tinytest_set_flag(groups, named, TT_SKIP)
+
+/** Run a single testcase in a single group. */
+int testcase_run_one(const struct testgroup_t *,const struct testcase_t *);
+/** Run a set of testcases from an END_OF_GROUPS-terminated array of groups,
+ as selected from the command line. */
+int tinytest_main(int argc, const char **argv, struct testgroup_t *groups);
+
+#endif
--- /dev/null
+/* tinytest_demo.c -- Copyright 2009 Nick Mathewson
+ *
+ * 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.
+ */
+
+
+/* Welcome to the example file for tinytest! I'll show you how to set up
+ * some simple and not-so-simple testcases. */
+
+/* Make sure you include these headers. */
+#include "tinytest.h"
+#include "tinytest_macros.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+/* ============================================================ */
+
+/* First, let's see if strcmp is working. (All your test cases should be
+ * functions declared to take a single void * as) an argument. */
+void
+test_strcmp(void *data)
+{
+ (void)data; /* This testcase takes no data. */
+
+ /* Let's make sure the empty string is equal to itself */
+ if (strcmp("","")) {
+ /* This macro tells tinytest to stop the current test
+ * and go straight to the "end" label. */
+ tt_abort_msg("The empty string was not equal to itself");
+ }
+
+ /* Pretty often, calling tt_abort_msg to indicate failure is more
+ heavy-weight than you want. Instead, just say: */
+ tt_assert(strcmp("testcase", "testcase") == 0);
+
+ /* Occasionally, you don't want to stop the current testcase just
+ because a single assertion has failed. In that case, use
+ tt_want: */
+ tt_want(strcmp("tinytest", "testcase") > 0);
+
+ /* You can use the tt_*_op family of macros to compare values and to
+ fail unless they have the relationship you want. They produce
+ more useful output than tt_assert, since they display the actual
+ values of the failing things.
+
+ Fail unless strcmp("abc, "abc") == 0 */
+ tt_int_op(strcmp("abc", "abc"), ==, 0);
+
+ /* Fail unless strcmp("abc, "abcd") is less than 0 */
+ tt_int_op(strcmp("abc", "abcd"), < , 0);
+
+ /* Incidentally, there's a test_str_op that uses strcmp internally. */
+ tt_str_op("abc", <, "abcd");
+
+
+ /* Every test-case function needs to finish with an "end:"
+ label and (optionally) code to clean up local variables. */
+ end:
+ ;
+}
+
+/* ============================================================ */
+
+/* Now let's mess with setup and teardown functions! These are handy if
+ you have a bunch of tests that all need a similar environment, and you
+ wnat to reconstruct that environment freshly for each one. */
+
+/* First you declare a type to hold the environment info, and functions to
+ set it up and tear it down. */
+struct data_buffer {
+ /* We're just going to have couple of character buffer. Using
+ setup/teardown functions is probably overkill for this case.
+
+ You could also do file descriptors, complicated handles, temporary
+ files, etc. */
+ char buffer1[512];
+ char buffer2[512];
+};
+/* The setup function needs to take a const struct testcase_t and return
+ void* */
+void *
+setup_data_buffer(const struct testcase_t *testcase)
+{
+ struct data_buffer *db = malloc(sizeof(struct data_buffer));
+
+ /* If you had a complicated set of setup rules, you might behave
+ differently here depending on testcase->flags or
+ testcase->setup_data or even or testcase->name. */
+
+ /* Returning a NULL here would mean that we couldn't set up for this
+ test, so we don't need to test db for null. */
+ return db;
+}
+/* The clean function deallocates storage carefully and returns true on
+ success. */
+int
+clean_data_buffer(const struct testcase_t *testcase, void *ptr)
+{
+ struct data_buffer *db = ptr;
+
+ if (db) {
+ free(db);
+ return 1;
+ }
+ return 0;
+}
+/* Finally, declare a testcase_setup_t with these functions. */
+struct testcase_setup_t data_buffer_setup = {
+ setup_data_buffer, clean_data_buffer
+};
+
+
+/* Now let's write our test. */
+void
+test_memcpy(void *ptr)
+{
+ /* This time, we use the argument. */
+ struct data_buffer *db = ptr;
+
+ /* We'll also introduce a local variable that might need cleaning up. */
+ char *mem = NULL;
+
+ /* Let's make sure that memcpy does what we'd like. */
+ strcpy(db->buffer1, "String 0");
+ memcpy(db->buffer2, db->buffer1, sizeof(db->buffer1));
+ tt_str_op(db->buffer1, ==, db->buffer2);
+
+ /* Now we've allocated memory that's referenced by a local variable.
+ The end block of the function will clean it up. */
+ mem = strdup("Hello world.");
+ tt_assert(mem);
+
+ /* Another rather trivial test. */
+ tt_str_op(db->buffer1, !=, mem);
+
+ end:
+ /* This time our end block has something to do. */
+ if (mem)
+ free(mem);
+}
+
+/* ============================================================ */
+
+/* Now we need to make sure that our tests get invoked. First, you take
+ a bunch of related tests and put them into an array of struct testcase_t.
+*/
+
+struct testcase_t demo_tests[] = {
+ /* Here's a really simple test: it has a name you can refer to it
+ with, and a function to invoke it. */
+ { "strcmp", test_strcmp, },
+
+ /* The second test has a flag, "TT_FORK", to make it run in a
+ subprocess, and a pointer to the testcase_setup_t that configures
+ its environment. */
+ { "memcpy", test_memcpy, TT_FORK, &data_buffer_setup },
+
+ /* The array has to end with END_OF_TESTCASES. */
+ END_OF_TESTCASES
+};
+
+/* Next, we make an array of testgroups. This is mandatory. Unlike more
+ heavy-duty testing frameworks, groups can't next. */
+struct testgroup_t groups[] = {
+
+ /* Every group has a 'prefix', and an array of tests. That's it. */
+ { "demo/", demo_tests },
+
+ END_OF_GROUPS
+};
+
+
+int
+main(int c, const char **v)
+{
+ /* Finally, just call tinytest_main(). It lets you specify verbose
+ or quiet output with --verbose and --quiet. You can list
+ specific tests:
+
+ tinytest-demo demo/memcpy
+
+ or use a ..-wildcard to select multiple tests with a common
+ prefix:
+
+ tinytest-demo demo/..
+
+ If you list no tests, you get them all by default, so that
+ "tinytest-demo" and "tinytest-demo .." mean the same thing.
+
+ */
+ return tinytest_main(c, v, groups);
+}
--- /dev/null
+/* tinytest_macros.h -- Copyright 2009 Nick Mathewson
+ *
+ * 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.
+ */
+
+#ifndef _TINYTEST_MACROS_H
+#define _TINYTEST_MACROS_H
+
+/* Helpers for defining statement-like macros */
+#define TT_STMT_BEGIN do {
+#define TT_STMT_END } while(0)
+
+/* Redefine this if your test functions want to abort with something besides
+ * "goto end;" */
+#ifndef TT_EXIT_TEST_FUNCTION
+#define TT_EXIT_TEST_FUNCTION TT_STMT_BEGIN goto end; TT_STMT_END
+#endif
+
+/* Redefine this if you want to note success/failure in some different way. */
+#ifndef TT_DECLARE
+#define TT_DECLARE(prefix, args) \
+ TT_STMT_BEGIN \
+ printf("\n %s %s:%d: ",prefix,__FILE__,__LINE__); \
+ printf args ; \
+ TT_STMT_END
+#endif
+
+/* Announce a failure. Args are parenthesized printf args. */
+#define TT_GRIPE(args) TT_DECLARE("FAIL", args)
+
+/* Announce a non-failure if we're verbose. */
+#define TT_BLATHER(args) \
+ TT_STMT_BEGIN \
+ if (_tinytest_get_verbosity()>1) TT_DECLARE(" OK", args); \
+ TT_STMT_END
+
+#define TT_DIE(args) \
+ TT_STMT_BEGIN \
+ _tinytest_set_test_failed(); \
+ TT_GRIPE(args); \
+ TT_EXIT_TEST_FUNCTION; \
+ TT_STMT_END
+
+#define TT_FAIL(args) \
+ TT_STMT_BEGIN \
+ _tinytest_set_test_failed(); \
+ TT_GRIPE(args); \
+ TT_STMT_END
+
+/* Fail and abort the current test for the reason in msg */
+#define tt_abort_msg(msg) TT_DIE((msg))
+#define tt_abort() tt_fail_msg("(Failed.)")
+
+/* Fail but do not abort the current test for the reason in msg. */
+#define tt_fail_msg(msg) TT_FAIL((msg))
+#define tt_fail() tt_fail_msg("(Failed.)")
+
+#define _tt_want(b, msg, fail) \
+ TT_STMT_BEGIN \
+ if (!(b)) { \
+ _tinytest_set_test_failed(); \
+ TT_GRIPE((msg)); \
+ fail; \
+ } else { \
+ TT_BLATHER((msg)); \
+ } \
+ TT_STMT_END
+
+/* Assert b, but do not stop the test if b fails. Log msg on failure. */
+#define tt_want_msg(b, msg) \
+ _tt_want(b, msg, );
+
+/* Assert b and stop the test if b fails. Log msg on failure. */
+#define tt_assert_msg(b, msg) \
+ _tt_want(b, msg, TT_EXIT_TEST_FUNCTION);
+
+/* Assert b, but do not stop the test if b fails. */
+#define tt_want(b) tt_want_msg( (b), "want("#b")")
+/* Assert b, and stop the test if b fails. */
+#define tt_assert(b) tt_assert_msg((b), "assert("#b")")
+
+#define tt_assert_test_type(a,b,str_test,type,test,fmt) \
+ TT_STMT_BEGIN \
+ type _val1 = (type)(a); \
+ type _val2 = (type)(b); \
+ if (!(test)) { \
+ TT_DIE(("assert(%s): "fmt" vs "fmt, \
+ str_test, _val1, _val2)); \
+ } else { \
+ TT_BLATHER(("assert(%s): "fmt" vs "fmt, \
+ str_test, _val1, _val2)); \
+ } \
+ TT_STMT_END
+
+/* Helper: assert that a op b, when cast to type. Format the values with
+ * printf format fmt on failure. */
+#define tt_assert_op_type(a,op,b,type,fmt) \
+ tt_assert_test_type(a,b,#a" "#op" "#b,type,(_val1 op _val2),fmt)
+
+#define tt_int_op(a,op,b) \
+ tt_assert_test_type(a,b,#a" "#op" "#b,long,(_val1 op _val2),"%ld")
+
+#define tt_uint_op(a,op,b) \
+ tt_assert_test_type(a,b,#a" "#op" "#b,unsigned long, \
+ (_val1 op _val2),"%lu")
+
+#define tt_str_op(a,op,b) \
+ tt_assert_test_type(a,b,#a" "#op" "#b,const char *, \
+ (strcmp(_val1,_val2) op 0),"<%s>")
+
+/** Fail and log the errno as with perror. */
+#define tt_fail_perror(op) \
+ TT_STMT_BEGIN \
+ TT_DIE(("%s: %s [%d]",(op),strerror(errno),errno)); \
+ TT_STMT_END
+
+#endif