diff options
author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-12-02 10:06:21 +0100 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-12-02 10:06:21 +0100 |
commit | fd841e416881cc0392e61ec312c1870f3a0004bd (patch) | |
tree | 8357ba56e79d614ba57f722e7878b853591dc339 /tests/libtap |
Initial import of libmongo-client version 0.1.8-2
Diffstat (limited to 'tests/libtap')
-rw-r--r-- | tests/libtap/Makefile.am | 4 | ||||
-rw-r--r-- | tests/libtap/tap.c | 298 | ||||
-rw-r--r-- | tests/libtap/tap.h | 85 | ||||
-rw-r--r-- | tests/libtap/test.c | 183 | ||||
-rw-r--r-- | tests/libtap/test.h | 84 |
5 files changed, 654 insertions, 0 deletions
diff --git a/tests/libtap/Makefile.am b/tests/libtap/Makefile.am new file mode 100644 index 0000000..271ade2 --- /dev/null +++ b/tests/libtap/Makefile.am @@ -0,0 +1,4 @@ +check_LTLIBRARIES = libtap.la +libtap_la_SOURCES = tap.c tap.h test.h test.c +libtap_la_CFLAGS = -I$(top_srcdir)/src/ @GLIB_CFLAGS@ +libtap_la_LIBADD = $(top_builddir)/src/libmongo-client.la @GLIB_LIBS@ diff --git a/tests/libtap/tap.c b/tests/libtap/tap.c new file mode 100644 index 0000000..e73ae4a --- /dev/null +++ b/tests/libtap/tap.c @@ -0,0 +1,298 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <glib.h> +#include "tap.h" + +static int expected_tests = NO_PLAN; +static int failed_tests; +static int current_test; +static char *todo_mesg; + +void +plan (int tests) { + expected_tests = tests; + if (tests != NO_PLAN) + printf("1..%d\n", tests); +} + +static char * +vstrdupf (const char *fmt, va_list args) { + char *str; + int size; + va_list args2; + va_copy(args2, args); + if (!fmt) + fmt = ""; + size = g_vsnprintf(NULL, 0, fmt, args2) + 2; + str = malloc(size); + vsprintf(str, fmt, args); + va_end(args2); + return str; +} + +int +vok_at_loc (const char *file, int line, int test, const char *fmt, + va_list args) +{ + char *name = vstrdupf(fmt, args); + printf("%sok %d", test ? "" : "not ", ++current_test); + if (*name) + printf(" - %s", name); + if (todo_mesg) { + printf(" # TODO"); + if (*todo_mesg) + printf(" %s", todo_mesg); + } + printf("\n"); + if (!test) { + if (*name) + diag(" Failed%s test '%s'\n at %s line %d.", + todo_mesg ? " (TODO)" : "", name, file, line); + else + diag(" Failed%s test at %s line %d.", + todo_mesg ? " (TODO)" : "", file, line); + if (!todo_mesg) + failed_tests++; + } + free(name); + return test; +} + +int +ok_at_loc (const char *file, int line, int test, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + vok_at_loc(file, line, test, fmt, args); + va_end(args); + return test; +} + +static int +mystrcmp (const char *a, const char *b) { + return a == b ? 0 : !a ? -1 : !b ? 1 : strcmp(a, b); +} + +#define eq(a, b) (!mystrcmp(a, b)) +#define ne(a, b) (mystrcmp(a, b)) + +int +is_at_loc (const char *file, int line, const char *got, const char *expected, + const char *fmt, ...) +{ + int test = eq(got, expected); + va_list args; + va_start(args, fmt); + vok_at_loc(file, line, test, fmt, args); + va_end(args); + if (!test) { + diag(" got: '%s'", got); + diag(" expected: '%s'", expected); + } + return test; +} + +int +isnt_at_loc (const char *file, int line, const char *got, const char *expected, + const char *fmt, ...) +{ + int test = ne(got, expected); + va_list args; + va_start(args, fmt); + vok_at_loc(file, line, test, fmt, args); + va_end(args); + if (!test) { + diag(" got: '%s'", got); + diag(" expected: anything else"); + } + return test; +} + +int +cmp_ok_at_loc (const char *file, int line, int a, const char *op, int b, + const char *fmt, ...) +{ + int test = eq(op, "||") ? a || b + : eq(op, "&&") ? a && b + : eq(op, "|") ? a | b + : eq(op, "^") ? a ^ b + : eq(op, "&") ? a & b + : eq(op, "==") ? a == b + : eq(op, "!=") ? a != b + : eq(op, "<") ? a < b + : eq(op, ">") ? a > b + : eq(op, "<=") ? a <= b + : eq(op, ">=") ? a >= b + : eq(op, "<<") ? a << b + : eq(op, ">>") ? a >> b + : eq(op, "+") ? a + b + : eq(op, "-") ? a - b + : eq(op, "*") ? a * b + : eq(op, "/") ? a / b + : eq(op, "%") ? a % b + : diag("unrecognized operator '%s'", op); + va_list args; + va_start(args, fmt); + vok_at_loc(file, line, test, fmt, args); + va_end(args); + if (!test) { + diag(" %d", a); + diag(" %s", op); + diag(" %d", b); + } + return test; +} + +static void +vdiag_to_fh (FILE *fh, const char *fmt, va_list args) { + char *mesg, *line; + int i; + if (!fmt) + return; + mesg = vstrdupf(fmt, args); + line = mesg; + for (i = 0; *line; i++) { + char c = mesg[i]; + if (!c || c == '\n') { + mesg[i] = '\0'; + fprintf(fh, "# %s\n", line); + if (!c) break; + mesg[i] = c; + line = &mesg[i+1]; + } + } + free(mesg); + return; +} + +int +diag (const char *fmt, ...) { + va_list args; + va_start(args, fmt); + vdiag_to_fh(stderr, fmt, args); + va_end(args); + return 0; +} + +int +note (const char *fmt, ...) { + va_list args; + va_start(args, fmt); + vdiag_to_fh(stdout, fmt, args); + va_end(args); + return 0; +} + +int +exit_status () { + int retval = 0; + if (expected_tests == NO_PLAN) { + printf("1..%d\n", current_test); + } + else if (current_test != expected_tests) { + diag("Looks like you planned %d test%s but ran %d.", + expected_tests, expected_tests > 1 ? "s" : "", current_test); + retval = 255; + } + if (failed_tests) { + diag("Looks like you failed %d test%s of %d run.", + failed_tests, failed_tests > 1 ? "s" : "", current_test); + if (expected_tests == NO_PLAN) + retval = failed_tests; + else + retval = expected_tests - current_test + failed_tests; + } + return retval; +} + +void +skippy (int n, const char *fmt, ...) { + char *why; + va_list args; + va_start(args, fmt); + why = vstrdupf(fmt, args); + va_end(args); + while (n --> 0) { + printf("ok %d ", ++current_test); + note("skip %s\n", why); + } + free(why); +} + +void +ctodo (int ignore __attribute__((unused)), const char *fmt, ...) { + va_list args; + va_start(args, fmt); + todo_mesg = vstrdupf(fmt, args); + va_end(args); +} + +void +cendtodo () { + free(todo_mesg); + todo_mesg = NULL; +} + +#ifndef _WIN32 +#include <sys/mman.h> +#include <regex.h> + +#ifdef __APPLE__ +#define MAP_ANONYMOUS MAP_ANON +#endif + +#ifndef MAP_ANON +#define MAP_ANON MAP_ANONYMOUS +#endif + +/* Create a shared memory int to keep track of whether a piece of code executed +dies. to be used in the dies_ok and lives_ok macros */ +int +tap_test_died (int status) { + static int *test_died = NULL; + int prev; + if (!test_died) { + test_died = (int *)mmap(0, sizeof (int), PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANON, -1, 0); + *test_died = 0; + } + prev = *test_died; + *test_died = status; + return prev; +} + +int +like_at_loc (int for_match, const char *file, int line, const char *got, + const char *expected, const char *fmt, ...) +{ + int test; + regex_t re; + int err = regcomp(&re, expected, REG_EXTENDED); + if (err) { + char errbuf[256]; + regerror(err, &re, errbuf, sizeof errbuf); + fprintf(stderr, "Unable to compile regex '%s': %s at %s line %d\n", + expected, errbuf, file, line); + exit(255); + } + err = regexec(&re, got, 0, NULL, 0); + regfree(&re); + test = for_match ? !err : err; + va_list args; + va_start(args, fmt); + vok_at_loc(file, line, test, fmt, args); + va_end(args); + if (!test) { + if (for_match) { + diag(" '%s'", got); + diag(" doesn't match: '%s'", expected); + } + else { + diag(" '%s'", got); + diag(" matches: '%s'", expected); + } + } + return test; +} +#endif diff --git a/tests/libtap/tap.h b/tests/libtap/tap.h new file mode 100644 index 0000000..3e841bc --- /dev/null +++ b/tests/libtap/tap.h @@ -0,0 +1,85 @@ +#ifndef __TAP_H__ +#define __TAP_H__ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> + +#define NO_PLAN -1 +#define ok(...) ok_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL) +#define pass(...) ok(1, ## __VA_ARGS__) +#define fail(...) ok(0, ## __VA_ARGS__) +#define is(...) is_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL) +#define isnt(...) isnt_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL) +#define cmp_ok(...) cmp_ok_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL) + +int vok_at_loc (const char *file, int line, int test, const char *fmt, + va_list args); +void plan (int tests); +int ok_at_loc (const char *file, int line, int test, const char *fmt, + ...); +int diag (const char *fmt, ...); +int note (const char *fmt, ...); +int exit_status (void); +void skippy (int n, const char *fmt, ...); +void ctodo (int ignore, const char *fmt, ...); +void cendtodo (void); +int is_at_loc (const char *file, int line, const char *got, + const char *expected, const char *fmt, ...); +int isnt_at_loc (const char *file, int line, const char *got, + const char *expected, const char *fmt, ...); +int cmp_ok_at_loc (const char *file, int line, int a, const char *op, + int b, const char *fmt, ...); + +#ifdef _WIN32 +#define like(...) skippy(1, "like is not implemented on MSWin32") +#define unlike(...) like() +#else +#define like(...) like_at_loc(1, __FILE__, __LINE__, __VA_ARGS__, NULL) +#define unlike(...) like_at_loc(0, __FILE__, __LINE__, __VA_ARGS__, NULL) +int like_at_loc (int for_match, const char *file, int line, + const char *got, const char *expected, + const char *fmt, ...); +#endif + +#define skip(test, ...) do {if (test) {skippy(__VA_ARGS__, NULL); break;} +#define endskip } while (0) + +#define todo(...) ctodo(0, ## __VA_ARGS__, NULL) +#define endtodo cendtodo() + +#define dies_ok(code, ...) dies_ok_common(code, 1, ## __VA_ARGS__) +#define lives_ok(code, ...) dies_ok_common(code, 0, ## __VA_ARGS__) + +#ifdef _WIN32 +#define dies_ok_common(...) \ + skippy(1, "Death detection is not supported on MSWin32") +#else +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +int tap_test_died (int status); +#define dies_ok_common(code, for_death, ...) \ + do { \ + tap_test_died(1); \ + int cpid = fork(); \ + switch (cpid) { \ + case -1: \ + perror("fork error"); \ + exit(EXIT_FAILURE); \ + case 0: /* child */ \ + close(1); close(2); \ + code \ + tap_test_died(0); \ + exit(EXIT_SUCCESS); \ + } \ + if (waitpid(cpid, NULL, 0) < 0) { \ + perror("waitpid error"); \ + exit(EXIT_FAILURE); \ + } \ + int it_died = tap_test_died(0); \ + if (!it_died) {code} \ + ok(for_death ? it_died : !it_died, ## __VA_ARGS__); \ + } while (0) +#endif +#endif diff --git a/tests/libtap/test.c b/tests/libtap/test.c new file mode 100644 index 0000000..979807f --- /dev/null +++ b/tests/libtap/test.c @@ -0,0 +1,183 @@ +#include "test.h" +#include "bson.h" +#include "mongo-utils.h" + +#include <glib.h> +#include <string.h> + +#ifndef HAVE_MSG_NOSIGNAL +#include <signal.h> +#endif + + +func_config_t config; + +bson * +test_bson_generate_full (void) +{ + bson *b, *d, *a, *scope; + guint8 oid[] = "1234567890ab"; + + a = bson_new (); + bson_append_int32 (a, "0", 32); + bson_append_int64 (a, "1", (gint64)-42); + bson_finish (a); + + d = bson_new (); + bson_append_string (d, "name", "sub-document", -1); + bson_append_int32 (d, "answer", 42); + bson_finish (d); + + scope = bson_new (); + bson_append_string (scope, "v", "hello world", -1); + bson_finish (scope); + + b = bson_new (); + bson_append_double (b, "double", 3.14); + bson_append_string (b, "str", "hello world", -1); + bson_append_document (b, "doc", d); + bson_append_array (b, "array", a); + bson_append_binary (b, "binary0", BSON_BINARY_SUBTYPE_GENERIC, + (guint8 *)"foo\0bar", 7); + bson_append_oid (b, "_id", oid); + bson_append_boolean (b, "TRUE", FALSE); + bson_append_utc_datetime (b, "date", 1294860709000); + bson_append_timestamp (b, "ts", 1294860709000); + bson_append_null (b, "null"); + bson_append_regex (b, "foobar", "s/foo.*bar/", "i"); + bson_append_javascript (b, "alert", "alert (\"hello world!\");", -1); + bson_append_symbol (b, "sex", "Marilyn Monroe", -1); + bson_append_javascript_w_scope (b, "print", "alert (v);", -1, scope); + bson_append_int32 (b, "int32", 32); + bson_append_int64 (b, "int64", (gint64)-42); + bson_finish (b); + + bson_free (d); + bson_free (a); + bson_free (scope); + + return b; +} + +mongo_packet * +test_mongo_wire_generate_reply (gboolean valid, gint32 nreturn, + gboolean with_docs) +{ + mongo_reply_packet_header rh; + mongo_packet_header h; + mongo_packet *p; + guint8 *data; + gint data_size = sizeof (mongo_reply_packet_header); + bson *b1 = NULL, *b2 = NULL; + + p = mongo_wire_packet_new (); + + h.opcode = (valid) ? GINT32_TO_LE (1) : GINT32_TO_LE (42); + h.id = GINT32_TO_LE (1984); + h.resp_to = GINT32_TO_LE (42); + if (with_docs) + { + b1 = test_bson_generate_full (); + b2 = test_bson_generate_full (); + data_size += bson_size (b1) + bson_size (b2); + } + h.length = GINT32_TO_LE (sizeof (mongo_packet_header) + data_size); + + mongo_wire_packet_set_header (p, &h); + + data = g_try_malloc (data_size); + + rh.flags = 0; + rh.cursor_id = GINT64_TO_LE ((gint64)12345); + rh.start = 0; + rh.returned = GINT32_TO_LE (nreturn); + + memcpy (data, &rh, sizeof (mongo_reply_packet_header)); + if (with_docs) + { + memcpy (data + sizeof (mongo_reply_packet_header), + bson_data (b1), bson_size (b1)); + memcpy (data + sizeof (mongo_reply_packet_header) + bson_size (b1), + bson_data (b2), bson_size (b2)); + } + + mongo_wire_packet_set_data (p, data, data_size); + g_free (data); + bson_free (b1); + bson_free (b2); + + return p; +} + +mongo_sync_connection * +test_make_fake_sync_conn (gint fd, gboolean slaveok) +{ + mongo_sync_connection *c; + + c = g_try_new0 (mongo_sync_connection, 1); + if (!c) + return NULL; + + c->super.fd = fd; + c->slaveok = slaveok; + c->safe_mode = FALSE; + c->auto_reconnect = FALSE; + c->max_insert_size = MONGO_SYNC_DEFAULT_MAX_INSERT_SIZE; + + return c; +} + +gboolean +test_env_setup (void) +{ + config.primary_host = config.secondary_host = NULL; + config.primary_port = config.secondary_port = 27017; + config.db = g_strdup ("test"); + config.coll = g_strdup ("libmongo"); + + if (getenv ("TEST_DB")) + { + g_free (config.db); + config.db = g_strdup (getenv ("TEST_DB")); + } + if (getenv ("TEST_COLLECTION")) + { + g_free (config.coll); + config.coll = g_strdup (getenv ("TEST_COLLECTION")); + } + config.ns = g_strconcat (config.db, ".", config.coll, NULL); + + config.gfs_prefix = g_strconcat (config.ns, ".", "grid", NULL); + + if (!getenv ("TEST_PRIMARY") || strlen (getenv ("TEST_PRIMARY")) == 0) + return FALSE; + + if (!mongo_util_parse_addr (getenv ("TEST_PRIMARY"), &config.primary_host, + &config.primary_port)) + return FALSE; + + if (getenv ("TEST_SECONDARY") && strlen (getenv ("TEST_SECONDARY")) > 0) + mongo_util_parse_addr (getenv ("TEST_SECONDARY"), &config.secondary_host, + &config.secondary_port); + + return TRUE; +} + +void +test_env_free (void) +{ + g_free (config.primary_host); + g_free (config.secondary_host); + g_free (config.db); + g_free (config.coll); + g_free (config.ns); + g_free (config.gfs_prefix); +} + +void +test_main_setup (void) +{ + #ifndef HAVE_MSG_NOSIGNAL + signal(SIGPIPE, SIG_IGN); + #endif +} diff --git a/tests/libtap/test.h b/tests/libtap/test.h new file mode 100644 index 0000000..1c442f5 --- /dev/null +++ b/tests/libtap/test.h @@ -0,0 +1,84 @@ +#ifndef LIBMONGO_CLIENT_TEST_H +#define LIBMONGO_CLIENT_TEST_H 1 + +#include "tap.h" +#include "bson.h" +#include "mongo-wire.h" +#include "mongo-sync.h" +#include "libmongo-private.h" + +#include <dlfcn.h> + +typedef struct +{ + gchar *primary_host; + gint primary_port; + + gchar *secondary_host; + gint secondary_port; + + gchar *db; + gchar *coll; + gchar *ns; + + gchar *gfs_prefix; +} func_config_t; + +extern func_config_t config; + +#define begin_network_tests(n) \ + do \ + { \ + skip(!test_env_setup (), n, "Environment not set up for network tests") + +#define end_network_tests() \ + endskip; \ + test_env_free(); \ + } while (0) + +#define RUN_TEST(n, t) \ + int \ + main (void) \ + { \ + test_main_setup(); \ + plan (n); \ + test_##t (); \ + return 0; \ + } + +gboolean test_env_setup (void); +void test_env_free (void); +void test_main_setup (void); + +#define RUN_NET_TEST(n, t) \ + int \ + main (void) \ + { \ + test_main_setup(); \ + if (!test_env_setup ()) \ + printf ("1..0 # skip, Environment not set up for network tests"); \ + else \ + { \ + plan (n); \ + test_##t (); \ + } \ + test_env_free (); \ + return 0; \ + } + +bson *test_bson_generate_full (void); +mongo_packet *test_mongo_wire_generate_reply (gboolean valid, + gint32 nreturn, + gboolean with_docs); +mongo_sync_connection *test_make_fake_sync_conn (gint fd, + gboolean slaveok); + +#define SAVE_OLD_FUNC(n) \ + static void *(*func_##n)(); \ + if (!func_##n) \ + func_##n = (void *(*)())dlsym (RTLD_NEXT, #n); + +#define CALL_OLD_FUNC(n, ...) \ + func_##n (__VA_ARGS__) + +#endif |