mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-10 04:50:37 -04:00
Refactor unit tests using my spiffy new "tinytest" framework.
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
This commit is contained in:
parent
39c8dbe073
commit
a8203b3490
@ -1,11 +1,12 @@
|
||||
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
|
||||
@ -16,9 +17,10 @@ test_weof_SOURCES = test-weof.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
|
||||
@ -30,6 +32,7 @@ regress_LDADD = ../libevent.la $(PTHREAD_LIBS) $(ZLIB_LIBS)
|
||||
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
|
||||
|
282
test/regress.c
282
test/regress.c
@ -75,7 +75,9 @@
|
||||
|
||||
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;
|
||||
@ -83,7 +85,8 @@ 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
|
||||
@ -255,6 +258,8 @@ combined_write_cb(int fd, short event, void *arg)
|
||||
static int
|
||||
setup_test(const char *name)
|
||||
{
|
||||
if (in_legacy_test_wrapper)
|
||||
return 0;
|
||||
|
||||
fprintf(stdout, "%s", name);
|
||||
|
||||
@ -277,6 +282,9 @@ setup_test(const char *name)
|
||||
static int
|
||||
cleanup_test(void)
|
||||
{
|
||||
if (in_legacy_test_wrapper)
|
||||
return 0;
|
||||
|
||||
#ifndef WIN32
|
||||
close(pair[0]);
|
||||
close(pair[1]);
|
||||
@ -491,8 +499,6 @@ test_persistent_timeout(void)
|
||||
struct event ev;
|
||||
int count = 0;
|
||||
|
||||
setup_test("Periodic timeout via EV_PERSIST: ");
|
||||
|
||||
timerclear(&tv);
|
||||
tv.tv_usec = 10000;
|
||||
|
||||
@ -504,7 +510,6 @@ test_persistent_timeout(void)
|
||||
|
||||
event_del(&ev);
|
||||
|
||||
cleanup_test();
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
@ -1077,7 +1082,7 @@ test_nonpersist_readd(void)
|
||||
cleanup_test();
|
||||
}
|
||||
|
||||
/* validates that an evbuffer is good */
|
||||
/* Validates that an evbuffer is good. */
|
||||
static void
|
||||
evbuffer_validate(struct evbuffer *buf)
|
||||
{
|
||||
@ -1520,7 +1525,7 @@ test_evbuffer_iterative(void)
|
||||
}
|
||||
|
||||
static void
|
||||
test_evbuffer_find(void)
|
||||
test_evbuffer_find(void *ptr)
|
||||
{
|
||||
u_char* p;
|
||||
const char* test1 = "1234567890\r\n";
|
||||
@ -1531,7 +1536,6 @@ test_evbuffer_find(void)
|
||||
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));
|
||||
@ -1539,18 +1543,12 @@ test_evbuffer_find(void)
|
||||
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)
|
||||
@ -1559,24 +1557,16 @@ test_evbuffer_find(void)
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1832,14 +1822,12 @@ test_priorities_cb(int fd, short what, void *arg)
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@ -1881,10 +1869,19 @@ test_priorities(int 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)
|
||||
{
|
||||
@ -2282,7 +2279,6 @@ 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)
|
||||
@ -2298,142 +2294,139 @@ test_evutil_strtoll(void)
|
||||
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);
|
||||
}
|
||||
tt_assert(base);
|
||||
|
||||
if (strcmp(backend, event_base_get_method(base)) == 0) {
|
||||
fprintf(stdout, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
tt_str_op(backend, !=, event_base_get_method(base));
|
||||
|
||||
event_base_free(base);
|
||||
event_config_free(cfg);
|
||||
done:
|
||||
fprintf(stdout, "OK\n");
|
||||
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
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
#ifdef WIN32
|
||||
WORD wVersionRequested;
|
||||
WSADATA wsaData;
|
||||
int err;
|
||||
struct testcase_t legacy_testcases[] = {
|
||||
/* Some converted-over tests */
|
||||
{ "methods", test_methods, TT_FORK, NULL, NULL },
|
||||
{ "evutil_snprintf", test_evutil_snprintf, 0, NULL, NULL },
|
||||
|
||||
wVersionRequested = MAKEWORD( 2, 2 );
|
||||
/* 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),
|
||||
|
||||
err = WSAStartup( wVersionRequested, &wsaData );
|
||||
#endif
|
||||
/* 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),
|
||||
|
||||
#ifndef WIN32
|
||||
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
|
||||
return (1);
|
||||
LEGACY(fork, TT_ISOLATED),
|
||||
#endif
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
|
||||
test_methods();
|
||||
END_OF_TESTCASES
|
||||
};
|
||||
|
||||
struct testcase_t signal_testcases[] = {
|
||||
#ifndef WIN32
|
||||
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
|
||||
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
|
||||
@ -2442,32 +2435,6 @@ main (int argc, char **argv)
|
||||
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
|
||||
@ -2476,27 +2443,10 @@ main (int argc, char **argv)
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,15 @@
|
||||
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);
|
||||
|
||||
@ -38,13 +47,30 @@ void rpc_suite(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
|
||||
|
@ -22,7 +22,6 @@
|
||||
|
||||
#include "regress.h"
|
||||
|
||||
static int called = 0;
|
||||
static int was_et = 0;
|
||||
|
||||
static void
|
||||
|
195
test/regress_main.c
Normal file
195
test/regress_main.c
Normal file
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
|
@ -49,7 +49,7 @@
|
||||
#include "event2/util.h"
|
||||
#include "../ipv6-internal.h"
|
||||
|
||||
void util_suite(void);
|
||||
#include "regress.h"
|
||||
|
||||
enum entry_status { NORMAL, CANONICAL, BAD };
|
||||
|
||||
@ -97,12 +97,9 @@ static struct ipv6_entry {
|
||||
};
|
||||
|
||||
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];
|
||||
@ -111,54 +108,44 @@ regress_ipv4_parse(void)
|
||||
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];
|
||||
@ -167,16 +154,13 @@ regress_ipv6_parse(void)
|
||||
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) {
|
||||
@ -187,8 +171,7 @@ regress_ipv6_parse(void)
|
||||
(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;
|
||||
}
|
||||
}
|
||||
@ -196,25 +179,18 @@ regress_ipv6_parse(void)
|
||||
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
|
||||
}
|
||||
|
||||
@ -235,10 +211,9 @@ static struct sa_port_ent {
|
||||
};
|
||||
|
||||
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) {
|
||||
@ -246,15 +221,11 @@ regress_sockaddr_port_parse(void)
|
||||
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) {
|
||||
@ -267,11 +238,9 @@ regress_sockaddr_port_parse(void)
|
||||
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;
|
||||
@ -283,26 +252,19 @@ regress_sockaddr_port_parse(void)
|
||||
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,
|
||||
};
|
||||
|
||||
|
||||
|
314
test/tinytest.c
Normal file
314
test/tinytest.c
Normal file
@ -0,0 +1,314 @@
|
||||
/* 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;
|
||||
}
|
||||
|
85
test/tinytest.h
Normal file
85
test/tinytest.h
Normal file
@ -0,0 +1,85 @@
|
||||
/* 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
|
215
test/tinytest_demo.c
Normal file
215
test/tinytest_demo.c
Normal file
@ -0,0 +1,215 @@
|
||||
/* 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);
|
||||
}
|
137
test/tinytest_macros.h
Normal file
137
test/tinytest_macros.h
Normal file
@ -0,0 +1,137 @@
|
||||
/* 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
|
Loading…
x
Reference in New Issue
Block a user