diff options
Diffstat (limited to 'src/ext')
-rw-r--r-- | src/ext/Makefile.nmake | 12 | ||||
-rw-r--r-- | src/ext/README | 7 | ||||
-rw-r--r-- | src/ext/csiphash.c | 166 | ||||
-rw-r--r-- | src/ext/eventdns.c | 17 | ||||
-rw-r--r-- | src/ext/ht.h | 11 | ||||
-rw-r--r-- | src/ext/include.am | 3 | ||||
-rw-r--r-- | src/ext/siphash.h | 13 | ||||
-rw-r--r-- | src/ext/tinytest.c | 157 | ||||
-rw-r--r-- | src/ext/tinytest.h | 19 | ||||
-rw-r--r-- | src/ext/tinytest_demo.c | 49 | ||||
-rw-r--r-- | src/ext/tinytest_macros.h | 27 |
11 files changed, 443 insertions, 38 deletions
diff --git a/src/ext/Makefile.nmake b/src/ext/Makefile.nmake new file mode 100644 index 0000000000..d02d03bf41 --- /dev/null +++ b/src/ext/Makefile.nmake @@ -0,0 +1,12 @@ +all: csiphash.lib + +CFLAGS = /O2 /MT /I ..\win32 /I ..\..\..\build-alpha\include /I ..\common \ + /I ..\ext + +CSIPHASH_OBJECTS = csiphash.obj + +csiphash.lib: $(CSIPHASH_OBJECTS) + lib $(CSIPHASH_OBJECTS) $(CURVE25519_DONNA_OBJECTS) /out:csiphash.lib + +clean: + del *.obj *.lib diff --git a/src/ext/README b/src/ext/README index 58ba7f699d..5d5a6e1518 100644 --- a/src/ext/README +++ b/src/ext/README @@ -42,3 +42,10 @@ curve25519_donna/*.c A copy of Adam Langley's curve25519-donna mostly-portable implementations of curve25519. + +csiphash.c +siphash.h + + Marek Majkowski's implementation of siphash 2-4, a secure keyed + hash algorithm to avoid collision-based DoS attacks against hash + tables. diff --git a/src/ext/csiphash.c b/src/ext/csiphash.c new file mode 100644 index 0000000000..c247886038 --- /dev/null +++ b/src/ext/csiphash.c @@ -0,0 +1,166 @@ +/* <MIT License> + Copyright (c) 2013 Marek Majkowski <marek@popcount.org> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + </MIT License> + + Original location: + https://github.com/majek/csiphash/ + + Solution inspired by code from: + Samuel Neves (supercop/crypto_auth/siphash24/little) + djb (supercop/crypto_auth/siphash24/little2) + Jean-Philippe Aumasson (https://131002.net/siphash/siphash24.c) +*/ + +#include "torint.h" +#include "siphash.h" +/* for tor_assert */ +#include "util.h" +/* for memcpy */ +#include <string.h> + +#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \ + __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define _le64toh(x) ((uint64_t)(x)) +#elif defined(_WIN32) +/* Windows is always little endian, unless you're on xbox360 + http://msdn.microsoft.com/en-us/library/b0084kay(v=vs.80).aspx */ +# define _le64toh(x) ((uint64_t)(x)) +#elif defined(__APPLE__) +# include <libkern/OSByteOrder.h> +# define _le64toh(x) OSSwapLittleToHostInt64(x) +#elif defined(sun) || defined(__sun) +# include <sys/byteorder.h> +# define _le64toh(x) LE_64(x) + +#else + +/* See: http://sourceforge.net/p/predef/wiki/Endianness/ */ +# if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +# include <sys/endian.h> +# else +# include <endian.h> +# endif +# if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN +# define _le64toh(x) ((uint64_t)(x)) +# else +# if defined(__OpenBSD__) +# define _le64toh(x) letoh64(x) +# else +# define _le64toh(x) le64toh(x) +# endif +# endif + +#endif + +#define ROTATE(x, b) (uint64_t)( ((x) << (b)) | ( (x) >> (64 - (b))) ) + +#define HALF_ROUND(a,b,c,d,s,t) \ + a += b; c += d; \ + b = ROTATE(b, s) ^ a; \ + d = ROTATE(d, t) ^ c; \ + a = ROTATE(a, 32); + +#define DOUBLE_ROUND(v0,v1,v2,v3) \ + HALF_ROUND(v0,v1,v2,v3,13,16); \ + HALF_ROUND(v2,v1,v0,v3,17,21); \ + HALF_ROUND(v0,v1,v2,v3,13,16); \ + HALF_ROUND(v2,v1,v0,v3,17,21); + +#if 0 +/* This does not seem to save very much runtime in the fast case, and it's + * potentially a big loss in the slow case where we're misaligned and we cross + * a cache line. */ +#if (defined(__i386) || defined(__i386__) || defined(_M_IX86) || \ + defined(__x86_64) || defined(__x86_64__) || \ + defined(_M_AMD64) || defined(_M_X64) || defined(__INTEL__)) +# define UNALIGNED_OK 1 +#endif +#endif + +uint64_t siphash24(const void *src, unsigned long src_sz, const struct sipkey *key) { + uint64_t k0 = key->k0; + uint64_t k1 = key->k1; + uint64_t b = (uint64_t)src_sz << 56; + const uint64_t *in = (uint64_t*)src; + + uint64_t t; + uint8_t *pt, *m; + + uint64_t v0 = k0 ^ 0x736f6d6570736575ULL; + uint64_t v1 = k1 ^ 0x646f72616e646f6dULL; + uint64_t v2 = k0 ^ 0x6c7967656e657261ULL; + uint64_t v3 = k1 ^ 0x7465646279746573ULL; + + while (src_sz >= 8) { +#ifdef UNALIGNED_OK + uint64_t mi = _le64toh(*in); +#else + uint64_t mi; + memcpy(&mi, in, 8); + mi = _le64toh(mi); +#endif + in += 1; src_sz -= 8; + v3 ^= mi; + DOUBLE_ROUND(v0,v1,v2,v3); + v0 ^= mi; + } + + t = 0; pt = (uint8_t*)&t; m = (uint8_t*)in; + switch (src_sz) { + case 7: pt[6] = m[6]; + case 6: pt[5] = m[5]; + case 5: pt[4] = m[4]; +#ifdef UNALIGNED_OK + case 4: *((uint32_t*)&pt[0]) = *((uint32_t*)&m[0]); break; +#else + case 4: pt[3] = m[3]; +#endif + case 3: pt[2] = m[2]; + case 2: pt[1] = m[1]; + case 1: pt[0] = m[0]; + } + b |= _le64toh(t); + + v3 ^= b; + DOUBLE_ROUND(v0,v1,v2,v3); + v0 ^= b; v2 ^= 0xff; + DOUBLE_ROUND(v0,v1,v2,v3); + DOUBLE_ROUND(v0,v1,v2,v3); + return (v0 ^ v1) ^ (v2 ^ v3); +} + + +static int the_siphash_key_is_set = 0; +static struct sipkey the_siphash_key; + +uint64_t siphash24g(const void *src, unsigned long src_sz) { + tor_assert(the_siphash_key_is_set); + return siphash24(src, src_sz, &the_siphash_key); +} + +void siphash_set_global_key(const struct sipkey *key) +{ + tor_assert(! the_siphash_key_is_set); + the_siphash_key.k0 = key->k0; + the_siphash_key.k1 = key->k1; + the_siphash_key_is_set = 1; +} diff --git a/src/ext/eventdns.c b/src/ext/eventdns.c index 66280cccdb..2b2988f1ec 100644 --- a/src/ext/eventdns.c +++ b/src/ext/eventdns.c @@ -842,10 +842,11 @@ name_parse(u8 *packet, int length, int *idx, char *name_out, size_t name_out_len } if (label_len > 63) return -1; if (cp != name_out) { - if (cp + 1 >= end) return -1; + if (cp >= name_out + name_out_len - 1) return -1; *cp++ = '.'; } - if (cp + label_len >= end) return -1; + if (label_len > name_out_len || + cp >= name_out + name_out_len - label_len) return -1; memcpy(cp, packet + j, label_len); cp += label_len; j += label_len; @@ -2298,6 +2299,10 @@ _evdns_nameserver_add_impl(const struct sockaddr *address, evtimer_set(&ns->timeout_event, nameserver_prod_callback, ns); +#if 1 + ns->socket = tor_open_socket_nonblocking(address->sa_family, SOCK_DGRAM, 0); + if (!SOCKET_OK(ns->socket)) { err = 1; goto out1; } +#else ns->socket = tor_open_socket(address->sa_family, SOCK_DGRAM, 0); if (ns->socket < 0) { err = 1; goto out1; } #ifdef _WIN32 @@ -2314,6 +2319,7 @@ _evdns_nameserver_add_impl(const struct sockaddr *address, } #endif +#endif /* 1 */ if (global_bind_addr_is_set && !sockaddr_is_loopback((struct sockaddr*)&global_bind_address)) { if (bind(ns->socket, (struct sockaddr *)&global_bind_address, @@ -3009,7 +3015,8 @@ resolv_conf_parse_line(char *const start, int flags) { if (!strcmp(first_token, "nameserver") && (flags & DNS_OPTION_NAMESERVERS)) { const char *const nameserver = NEXT_TOKEN; - evdns_nameserver_ip_add(nameserver); + if (nameserver) + evdns_nameserver_ip_add(nameserver); } else if (!strcmp(first_token, "domain") && (flags & DNS_OPTION_SEARCH)) { const char *const domain = NEXT_TOKEN; if (domain) { @@ -3473,8 +3480,12 @@ main(int c, char **v) { if (servertest) { int sock; struct sockaddr_in my_addr; +#if 1 + sock = tor_open_socket_nonblocking(PF_INET, SOCK_DGRAM, 0) +#else sock = tor_open_socket(PF_INET, SOCK_DGRAM, 0); fcntl(sock, F_SETFL, O_NONBLOCK); +#endif my_addr.sin_family = AF_INET; my_addr.sin_port = htons(10053); my_addr.sin_addr.s_addr = INADDR_ANY; diff --git a/src/ext/ht.h b/src/ext/ht.h index 62c458ad0e..838710784f 100644 --- a/src/ext/ht.h +++ b/src/ext/ht.h @@ -58,6 +58,7 @@ #define HT_NEXT_RMV(name, head, elm) name##_HT_NEXT_RMV((head), (elm)) #define HT_CLEAR(name, head) name##_HT_CLEAR(head) #define HT_INIT(name, head) name##_HT_INIT(head) +#define HT_REP_IS_BAD_(name, head) name##_HT_REP_IS_BAD_(head) /* Helper: */ static INLINE unsigned ht_improve_hash(unsigned h) @@ -86,6 +87,7 @@ ht_string_hash(const char *s) } #endif +#if 0 /** Basic string hash function, from Python's str.__hash__() */ static INLINE unsigned ht_string_hash(const char *s) @@ -100,6 +102,7 @@ ht_string_hash(const char *s) h ^= (unsigned)(cp-(const unsigned char*)s); return h; } +#endif #ifndef HT_NO_CACHE_HASH_VALUES #define HT_SET_HASH_(elm, field, hashfn) \ @@ -157,7 +160,7 @@ ht_string_hash(const char *s) } \ /* Return a pointer to the element in the table 'head' matching 'elm', \ * or NULL if no such element exists */ \ - static INLINE struct type * \ + ATTR_UNUSED static INLINE struct type * \ name##_HT_FIND(const struct name *head, struct type *elm) \ { \ struct type **p; \ @@ -301,14 +304,16 @@ ht_string_hash(const char *s) #define HT_GENERATE(name, type, field, hashfn, eqfn, load, mallocfn, \ reallocfn, freefn) \ + /* Primes that aren't too far from powers of two. We stop at */ \ + /* P=402653189 because P*sizeof(void*) is less than SSIZE_MAX */ \ + /* even on a 32-bit platform. */ \ static unsigned name##_PRIMES[] = { \ 53, 97, 193, 389, \ 769, 1543, 3079, 6151, \ 12289, 24593, 49157, 98317, \ 196613, 393241, 786433, 1572869, \ 3145739, 6291469, 12582917, 25165843, \ - 50331653, 100663319, 201326611, 402653189, \ - 805306457, 1610612741 \ + 50331653, 100663319, 201326611, 402653189 \ }; \ static unsigned name##_N_PRIMES = \ (unsigned)(sizeof(name##_PRIMES)/sizeof(name##_PRIMES[0])); \ diff --git a/src/ext/include.am b/src/ext/include.am index ea7e58e79e..26e194e88e 100644 --- a/src/ext/include.am +++ b/src/ext/include.am @@ -10,7 +10,8 @@ EXTHEADERS = \ src/ext/strlcat.c \ src/ext/strlcpy.c \ src/ext/tinytest_macros.h \ - src/ext/tor_queue.h + src/ext/tor_queue.h \ + src/ext/siphash.h noinst_HEADERS+= $(EXTHEADERS) diff --git a/src/ext/siphash.h b/src/ext/siphash.h new file mode 100644 index 0000000000..d9b34b8980 --- /dev/null +++ b/src/ext/siphash.h @@ -0,0 +1,13 @@ +#ifndef SIPHASH_H +#define SIPHASH_H + +struct sipkey { + uint64_t k0; + uint64_t k1; +}; +uint64_t siphash24(const void *src, unsigned long src_sz, const struct sipkey *key); + +void siphash_set_global_key(const struct sipkey *key); +uint64_t siphash24g(const void *src, unsigned long src_sz); + +#endif diff --git a/src/ext/tinytest.c b/src/ext/tinytest.c index 4d9afacce4..cc054ad340 100644 --- a/src/ext/tinytest.c +++ b/src/ext/tinytest.c @@ -31,6 +31,8 @@ #include <string.h> #include <assert.h> +#ifndef NO_FORKING + #ifdef _WIN32 #include <windows.h> #else @@ -39,6 +41,17 @@ #include <unistd.h> #endif +#if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) +#if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 && \ + __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070) +/* Workaround for a stupid bug in OSX 10.6 */ +#define FORK_BREAKS_GCOV +#include <vproc.h> +#endif +#endif + +#endif /* !NO_FORKING */ + #ifndef __GNUC__ #define __attribute__(x) #endif @@ -58,6 +71,8 @@ static int opt_nofork = 0; /**< Suppress calls to fork() for debugging. */ static int opt_verbosity = 1; /**< -==quiet,0==terse,1==normal,2==verbose */ const char *verbosity_flag = ""; +const struct testlist_alias_t *cfg_aliases=NULL; + enum outcome { SKIP=2, OK=1, FAIL=0 }; static enum outcome cur_test_outcome = 0; const char *cur_test_prefix = NULL; /**< prefix of the current test group */ @@ -71,6 +86,7 @@ static char commandname[MAX_PATH+1]; static void usage(struct testgroup_t *groups, int list_groups) __attribute__((noreturn)); +static int process_test_option(struct testgroup_t *groups, const char *test); static enum outcome testcase_run_bare_(const struct testcase_t *testcase) @@ -99,6 +115,8 @@ testcase_run_bare_(const struct testcase_t *testcase) #define MAGIC_EXITCODE 42 +#ifndef NO_FORKING + static enum outcome testcase_run_forked_(const struct testgroup_t *group, const struct testcase_t *testcase) @@ -160,6 +178,9 @@ testcase_run_forked_(const struct testgroup_t *group, if (opt_verbosity>0) printf("[forking] "); pid = fork(); +#ifdef FORK_BREAKS_GCOV + vproc_transaction_begin(0); +#endif if (!pid) { /* child. */ int test_r, write_r; @@ -196,16 +217,19 @@ testcase_run_forked_(const struct testgroup_t *group, #endif } +#endif /* !NO_FORKING */ + int testcase_run_one(const struct testgroup_t *group, const struct testcase_t *testcase) { enum outcome outcome; - if (testcase->flags & TT_SKIP) { + if (testcase->flags & (TT_SKIP|TT_OFF_BY_DEFAULT)) { if (opt_verbosity>0) - printf("%s%s: SKIPPED\n", - group->prefix, testcase->name); + printf("%s%s: %s\n", + group->prefix, testcase->name, + (testcase->flags & TT_SKIP) ? "SKIPPED" : "DISABLED"); ++n_skipped; return SKIP; } @@ -218,9 +242,13 @@ testcase_run_one(const struct testgroup_t *group, cur_test_name = testcase->name; } +#ifndef NO_FORKING if ((testcase->flags & TT_FORK) && !(opt_forked||opt_nofork)) { outcome = testcase_run_forked_(group, testcase); } else { +#else + { +#endif outcome = testcase_run_bare_(testcase); } @@ -247,7 +275,7 @@ testcase_run_one(const struct testgroup_t *group, } int -tinytest_set_flag_(struct testgroup_t *groups, const char *arg, unsigned long flag) +tinytest_set_flag_(struct testgroup_t *groups, const char *arg, int set, unsigned long flag) { int i, j; size_t length = LONGEST_TEST_NAME; @@ -257,12 +285,23 @@ tinytest_set_flag_(struct testgroup_t *groups, const char *arg, unsigned long fl length = strstr(arg,"..")-arg; for (i=0; groups[i].prefix; ++i) { for (j=0; groups[i].cases[j].name; ++j) { + struct testcase_t *testcase = &groups[i].cases[j]; snprintf(fullname, sizeof(fullname), "%s%s", - groups[i].prefix, groups[i].cases[j].name); - if (!flag) /* Hack! */ - printf(" %s\n", fullname); + groups[i].prefix, testcase->name); + if (!flag) { /* Hack! */ + printf(" %s", fullname); + if (testcase->flags & TT_OFF_BY_DEFAULT) + puts(" (Off by default)"); + else if (testcase->flags & TT_SKIP) + puts(" (DISABLED)"); + else + puts(""); + } if (!strncmp(fullname, arg, length)) { - groups[i].cases[j].flags |= flag; + if (set) + testcase->flags |= flag; + else + testcase->flags &= ~flag; ++found; } } @@ -275,15 +314,69 @@ usage(struct testgroup_t *groups, int list_groups) { puts("Options are: [--verbose|--quiet|--terse] [--no-fork]"); puts(" Specify tests by name, or using a prefix ending with '..'"); - puts(" To skip a test, list give its name prefixed with a colon."); + puts(" To skip a test, prefix its name with a colon."); + puts(" To enable a disabled test, prefix its name with a plus."); puts(" Use --list-tests for a list of tests."); if (list_groups) { puts("Known tests are:"); - tinytest_set_flag_(groups, "..", 0); + tinytest_set_flag_(groups, "..", 1, 0); } exit(0); } +static int +process_test_alias(struct testgroup_t *groups, const char *test) +{ + int i, j, n, r; + for (i=0; cfg_aliases && cfg_aliases[i].name; ++i) { + if (!strcmp(cfg_aliases[i].name, test)) { + n = 0; + for (j = 0; cfg_aliases[i].tests[j]; ++j) { + r = process_test_option(groups, cfg_aliases[i].tests[j]); + if (r<0) + return -1; + n += r; + } + return n; + } + } + printf("No such test alias as @%s!",test); + return -1; +} + +static int +process_test_option(struct testgroup_t *groups, const char *test) +{ + int flag = TT_ENABLED_; + int n = 0; + if (test[0] == '@') { + return process_test_alias(groups, test + 1); + } else if (test[0] == ':') { + ++test; + flag = TT_SKIP; + } else if (test[0] == '+') { + ++test; + ++n; + if (!tinytest_set_flag_(groups, test, 0, TT_OFF_BY_DEFAULT)) { + printf("No such test as %s!\n", test); + return -1; + } + } else { + ++n; + } + if (!tinytest_set_flag_(groups, test, 1, flag)) { + printf("No such test as %s!\n", test); + return -1; + } + return n; +} + +void +tinytest_set_aliases(const struct testlist_alias_t *aliases) +{ + cfg_aliases = aliases; +} + int tinytest_main(int c, const char **v, struct testgroup_t *groups) { @@ -321,24 +414,18 @@ tinytest_main(int c, const char **v, struct testgroup_t *groups) return -1; } } else { - const char *test = v[i]; - int flag = TT_ENABLED_; - if (test[0] == ':') { - ++test; - flag = TT_SKIP; - } else { - ++n; - } - if (!tinytest_set_flag_(groups, test, flag)) { - printf("No such test as %s!\n", v[i]); + int r = process_test_option(groups, v[i]); + if (r<0) return -1; - } + n += r; } } if (!n) - tinytest_set_flag_(groups, "..", TT_ENABLED_); + tinytest_set_flag_(groups, "..", 1, TT_ENABLED_); +#ifdef _IONBF setvbuf(stdout, NULL, _IONBF, 0); +#endif ++in_tinytest_main; for (i=0; groups[i].prefix; ++i) @@ -385,3 +472,29 @@ tinytest_set_test_skipped_(void) cur_test_outcome = SKIP; } +char * +tinytest_format_hex_(const void *val_, unsigned long len) +{ + const unsigned char *val = val_; + char *result, *cp; + size_t i; + int ellipses = 0; + + if (!val) + return strdup("null"); + if (len > 1024) { + ellipses = 3; + len = 1024; + } + if (!(result = malloc(len*2+4))) + return strdup("<allocation failure>"); + cp = result; + for (i=0;i<len;++i) { + *cp++ = "0123456789ABCDEF"[val[i] >> 4]; + *cp++ = "0123456789ABCDEF"[val[i] & 0x0f]; + } + while (ellipses--) + *cp++ = '.'; + *cp = 0; + return result; +} diff --git a/src/ext/tinytest.h b/src/ext/tinytest.h index bcac9f079c..ed07b26bc0 100644 --- a/src/ext/tinytest.h +++ b/src/ext/tinytest.h @@ -32,8 +32,10 @@ #define TT_SKIP (1<<1) /** Internal runtime flag for a test we've decided to run. */ #define TT_ENABLED_ (1<<2) +/** Flag for a test that's off by default. */ +#define TT_OFF_BY_DEFAULT (1<<3) /** If you add your own flags, make them start at this point. */ -#define TT_FIRST_USER_FLAG (1<<3) +#define TT_FIRST_USER_FLAG (1<<4) typedef void (*testcase_fn)(void *); @@ -64,6 +66,12 @@ struct testgroup_t { }; #define END_OF_GROUPS { NULL, NULL} +struct testlist_alias_t { + const char *name; + const char **tests; +}; +#define END_OF_ALIASES { NULL, NULL } + /** Implementation: called from a test to indicate failure, before logging. */ void tinytest_set_test_failed_(void); /** Implementation: called from a test to indicate that we're skipping. */ @@ -72,14 +80,19 @@ void tinytest_set_test_skipped_(void); 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); +int tinytest_set_flag_(struct testgroup_t *, const char *, int set, unsigned long); +/** Implementation: Put a chunk of memory into hex. */ +char *tinytest_format_hex_(const void *, 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) + tinytest_set_flag_(groups, named, 1, TT_SKIP) /** Run a single testcase in a single group. */ int testcase_run_one(const struct testgroup_t *,const struct testcase_t *); + +void tinytest_set_aliases(const struct testlist_alias_t *aliases); + /** 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); diff --git a/src/ext/tinytest_demo.c b/src/ext/tinytest_demo.c index be95ce4c1d..634e112cb8 100644 --- a/src/ext/tinytest_demo.c +++ b/src/ext/tinytest_demo.c @@ -35,6 +35,13 @@ #include <stdlib.h> #include <string.h> #include <errno.h> +#include <time.h> + +#ifdef _WIN32 +#include <windows.h> +#else +#include <unistd.h> +#endif /* ============================================================ */ @@ -148,6 +155,10 @@ test_memcpy(void *ptr) memcpy(db->buffer2, db->buffer1, sizeof(db->buffer1)); tt_str_op(db->buffer1, ==, db->buffer2); + /* tt_mem_op() does a memcmp, as opposed to the strcmp in tt_str_op() */ + db->buffer2[100] = 3; /* Make the buffers unequal */ + tt_mem_op(db->buffer1, <, db->buffer2, sizeof(db->buffer1)); + /* 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."); @@ -162,6 +173,27 @@ test_memcpy(void *ptr) free(mem); } +void +test_timeout(void *ptr) +{ + time_t t1, t2; + (void)ptr; + t1 = time(NULL); +#ifdef _WIN32 + Sleep(5000); +#else + sleep(5); +#endif + t2 = time(NULL); + + tt_int_op(t2-t1, >=, 4); + + tt_int_op(t2-t1, <=, 6); + + end: + ; +} + /* ============================================================ */ /* Now we need to make sure that our tests get invoked. First, you take @@ -178,6 +210,10 @@ struct testcase_t demo_tests[] = { its environment. */ { "memcpy", test_memcpy, TT_FORK, &data_buffer_setup }, + /* This flag is off-by-default, since it takes a while to run. You + * can enable it manually by passing +demo/timeout at the command line.*/ + { "timeout", test_timeout, TT_OFF_BY_DEFAULT }, + /* The array has to end with END_OF_TESTCASES. */ END_OF_TESTCASES }; @@ -192,6 +228,18 @@ struct testgroup_t groups[] = { END_OF_GROUPS }; +/* We can also define test aliases. These can be used for types of tests that + * cut across groups. */ +const char *alltests[] = { "+..", NULL }; +const char *slowtests[] = { "+demo/timeout", NULL }; +struct testlist_alias_t aliases[] = { + + { "ALL", alltests }, + { "SLOW", slowtests }, + + END_OF_ALIASES +}; + int main(int c, const char **v) @@ -211,5 +259,6 @@ main(int c, const char **v) "tinytest-demo" and "tinytest-demo .." mean the same thing. */ + tinytest_set_aliases(aliases); return tinytest_main(c, v, groups); } diff --git a/src/ext/tinytest_macros.h b/src/ext/tinytest_macros.h index 9ff69b1d50..c3728d1fdd 100644 --- a/src/ext/tinytest_macros.h +++ b/src/ext/tinytest_macros.h @@ -113,8 +113,8 @@ #define tt_assert_test_fmt_type(a,b,str_test,type,test,printf_type,printf_fmt, \ setup_block,cleanup_block,die_on_fail) \ TT_STMT_BEGIN \ - type val1_ = (type)(a); \ - type val2_ = (type)(b); \ + type val1_ = (a); \ + type val2_ = (b); \ int tt_status_ = (test); \ if (!tt_status_ || tinytest_get_verbosity_()>1) { \ printf_type print_; \ @@ -144,6 +144,10 @@ tt_assert_test_fmt_type(a,b,str_test,type,test,type,fmt, \ {print_=value_;},{},die_on_fail) +#define tt_assert_test_type_opt(a,b,str_test,type,test,fmt,die_on_fail) \ + tt_assert_test_fmt_type(a,b,str_test,type,test,type,fmt, \ + {print_=value_?value_:"<NULL>";},{},die_on_fail) + /* 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) \ @@ -159,12 +163,23 @@ (val1_ op val2_),"%lu",TT_EXIT_TEST_FUNCTION) #define tt_ptr_op(a,op,b) \ - tt_assert_test_type(a,b,#a" "#op" "#b,void*, \ + tt_assert_test_type(a,b,#a" "#op" "#b,const void*, \ (val1_ op val2_),"%p",TT_EXIT_TEST_FUNCTION) #define tt_str_op(a,op,b) \ - tt_assert_test_type(a,b,#a" "#op" "#b,const char *, \ - (strcmp(val1_,val2_) op 0),"<%s>",TT_EXIT_TEST_FUNCTION) + tt_assert_test_type_opt(a,b,#a" "#op" "#b,const char *, \ + (val1_ && val2_ && strcmp(val1_,val2_) op 0),"<%s>", \ + TT_EXIT_TEST_FUNCTION) + +#define tt_mem_op(expr1, op, expr2, len) \ + tt_assert_test_fmt_type(expr1,expr2,#expr1" "#op" "#expr2, \ + const void *, \ + (val1_ && val2_ && memcmp(val1_, val2_, len) op 0), \ + char *, "%s", \ + { print_ = tinytest_format_hex_(value_, (len)); }, \ + { if (print_) free(print_); }, \ + TT_EXIT_TEST_FUNCTION \ + ); #define tt_want_int_op(a,op,b) \ tt_assert_test_type(a,b,#a" "#op" "#b,long,(val1_ op val2_),"%ld",(void)0) @@ -174,7 +189,7 @@ (val1_ op val2_),"%lu",(void)0) #define tt_want_ptr_op(a,op,b) \ - tt_assert_test_type(a,b,#a" "#op" "#b,void*, \ + tt_assert_test_type(a,b,#a" "#op" "#b,const void*, \ (val1_ op val2_),"%p",(void)0) #define tt_want_str_op(a,op,b) \ |