diff --git a/t/utils/dbi_test.c b/t/utils/dbi_test.c
index 57449bb546ee5fd61a6d8d092712b6032ca2a6f4..c54ea3164dec8b965bb16a4d64368bbac6b3b694 100644 (file)
--- a/t/utils/dbi_test.c
+++ b/t/utils/dbi_test.c
#define TEST_MAGIC ((void *)0x1337)
+/*
+ * private data-types
+ */
+typedef union {
+ long long integer;
+ double decimal;
+ const char *string;
+ time_t datetime;
+ struct {
+ size_t length;
+ const unsigned char *datum;
+ } binary;
+} mock_data_t;
+
+typedef struct {
+ const char *name;
+ unsigned long long nrows;
+ unsigned long long current_row;
+ unsigned int nfields;
+ unsigned short *field_types;
+ char **field_names;
+} mock_query_t;
+
/*
* private variables
*/
static sdb_dbi_client_t *client;
+/*
+ * mock queries
+ */
+
+/* field definitions */
+static unsigned short field_types[] = {
+ DBI_TYPE_INTEGER,
+ DBI_TYPE_DECIMAL,
+ DBI_TYPE_STRING,
+ DBI_TYPE_DATETIME,
+ DBI_TYPE_BINARY,
+};
+static char *field_names[] = {
+ "field0",
+ "field1",
+ "field2",
+ "field3",
+ "field4",
+};
+
+#define DATUM(p) ((const unsigned char *)(p))
+static mock_data_t golden_data[][5] = {
+ {
+ { .integer = 1234 },
+ { .decimal = 1.234 },
+ { .string = "abcd" },
+ { .datetime = 0 },
+ { .binary = { 1, DATUM("a") } },
+ },
+ {
+ { .integer = 2345 },
+ { .decimal = 23.45 },
+ { .string = "bcde" },
+ { .datetime = 1 },
+ { .binary = { 4, DATUM("bcde") } },
+ },
+ {
+ { .integer = 3456 },
+ { .decimal = 345.6 },
+ { .string = "cd" },
+ { .datetime = 2 },
+ { .binary = { 0, DATUM(NULL) } },
+ },
+ {
+ { .integer = 4567 },
+ { .decimal = 4567 },
+ { .string = "d" },
+ { .datetime = 3 },
+ { .binary = { 13, DATUM("defghijklmnop") } },
+ },
+ {
+ { .integer = 5678 },
+ { .decimal = 56.78 },
+ { .string = "efgh" },
+ { .datetime = 4 },
+ { .binary = { 5, DATUM("efghi") } },
+ },
+};
+
+/* query definitions */
+static mock_query_t mock_queries[] = {
+ { "mockquery0", 5, 1, 0, NULL, NULL },
+ { "mockquery1", 0, 0, 1, field_types, field_names },
+ { "mockquery2", 1, 1, 1, field_types, field_names },
+ { "mockquery3", 2, 1, 1, field_types, field_names },
+ { "mockquery4", 5, 1, 1, field_types, field_names },
+ { "mockquery5", 5, 1, 2, field_types, field_names },
+ { "mockquery6", 5, 1, 3, field_types, field_names },
+ { "mockquery7", 5, 1, 4, field_types, field_names },
+ { "mockquery8", 5, 1, 5, field_types, field_names },
+};
+
+static mock_query_t *current_query = NULL;
+
/*
* mocked functions
*/
return (dbi_conn)"mockconnection";
} /* dbi_conn_open */
-static int dbi_conn_connect_called = 0;
+static unsigned long long dbi_conn_connect_called = 0;
int
dbi_conn_connect(dbi_conn conn)
{
return;
} /* dbi_conn_close */
-static int dbi_conn_query_called = 0;
+int
+dbi_conn_error(dbi_conn conn, const char **errmsg)
+{
+ if ((! conn) || (strcmp((const char *)conn, "mockconnection")))
+ return DBI_ERROR_BADOBJECT;
+ if (errmsg)
+ *errmsg = "mockerror";
+ return DBI_ERROR_UNSUPPORTED;
+} /* dbi_conn_error */
+
+static unsigned long long dbi_conn_query_called = 0;
dbi_result
dbi_conn_query(dbi_conn conn, const char __attribute__((unused)) *statement)
{
+ size_t i;
+
++dbi_conn_query_called;
if (strcmp((const char *)conn, "mockconnection"))
return NULL;
- return (dbi_result)"mockresult";
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(mock_queries); ++i) {
+ if (!strcmp(mock_queries[i].name, statement)) {
+ current_query = &mock_queries[i];
+ return (dbi_result)current_query;
+ }
+ }
+ return NULL;
} /* dbi_conn_query */
unsigned long long
dbi_result_get_numrows(dbi_result res)
{
- if (strcmp((const char *)res, "mockresult"))
+ mock_query_t *q = res;
+ if (! q)
return DBI_ROW_ERROR;
- return 5;
+ return q->nrows;
} /* dbi_result_get_numrows */
-static int dbi_result_free_called = 0;
-int
-dbi_result_free(dbi_result res)
-{
- ++dbi_result_free_called;
- if (strcmp((const char *)res, "mockresult"))
- return -1;
- return 0;
-} /* dbi_result_free */
-
unsigned int
dbi_result_get_numfields(dbi_result res)
{
- if (strcmp((const char *)res, "mockresult"))
+ mock_query_t *q = res;
+ if (! q)
return DBI_FIELD_ERROR;
- return 0;
+ return q->nfields;
} /* dbi_result_get_numfields */
unsigned short
-dbi_result_get_field_type_idx(dbi_result res,
- unsigned int __attribute__((unused)) i)
+dbi_result_get_field_type_idx(dbi_result res, unsigned int i)
{
- if (strcmp((const char *)res, "mockresult"))
+ mock_query_t *q = res;
+ if ((! q) || (i > q->nfields))
return DBI_TYPE_ERROR;
- return DBI_TYPE_ERROR;
+ return q->field_types[i - 1];
} /* dbi_result_get_field_type_idx */
const char *
-dbi_result_get_field_name(dbi_result res,
- unsigned int __attribute__((unused)) i)
+dbi_result_get_field_name(dbi_result res, unsigned int i)
{
- if (strcmp((const char *)res, "mockresult"))
+ mock_query_t *q = res;
+ if ((! q) || (i > q->nfields))
return NULL;
- return NULL;
+ return q->field_names[i - 1];
} /* dbi_result_get_field_name */
+int
+dbi_result_seek_row(dbi_result res, unsigned long long n)
+{
+ mock_query_t *q = res;
+ if ((! q) || (n > q->nrows))
+ return 0;
+
+ q->current_row = n;
+ return 1;
+} /* dbi_result_seek_row */
+
+static mock_data_t
+get_golden_data(dbi_result res, unsigned int i) {
+ mock_query_t *q = res;
+ fail_unless(q != NULL, "dbi_result_get_*_idx() called with "
+ "NULL result data; expected valid result object");
+
+ /* this information is managed by seek_row and, thus,
+ * should never be invalid */
+ fail_unless(q->current_row && q->current_row <= q->nrows,
+ "INTERNAL ERROR: current row out of range");
+
+ fail_unless(i && i <= q->nfields,
+ "dbi_result_get_*_idx() called with index out of range; "
+ "got: %u; expected [1, %u]", i, q->nfields);
+ return golden_data[q->current_row - 1][i - 1];
+} /* get_golden_data */
+
+long long
+dbi_result_get_longlong_idx(dbi_result res, unsigned int i)
+{
+ fail_unless(current_query->field_types[i - 1] == DBI_TYPE_INTEGER,
+ "dbi_result_get_longlong_idx() called for non-integer "
+ "column type %u", current_query->field_types[i - 1]);
+ return get_golden_data(res, i).integer;
+} /* dbi_result_get_longlong_idx */
+
+double
+dbi_result_get_double_idx(dbi_result res, unsigned int i)
+{
+ fail_unless(current_query->field_types[i - 1] == DBI_TYPE_DECIMAL,
+ "dbi_result_get_double_idx() called for non-decimal "
+ "column type %u", current_query->field_types[i - 1]);
+ return get_golden_data(res, i).decimal;
+} /* dbi_result_get_double_idx */
+
+const char *
+dbi_result_get_string_idx(dbi_result res, unsigned int i)
+{
+ fail_unless(current_query->field_types[i - 1] == DBI_TYPE_STRING,
+ "dbi_result_get_string_idx() called for non-string "
+ "column type %u", current_query->field_types[i - 1]);
+ return get_golden_data(res, i).string;
+} /* dbi_result_get_string_idx */
+
+char *
+dbi_result_get_string_copy_idx(dbi_result res, unsigned int i)
+{
+ fail_unless(current_query->field_types[i - 1] == DBI_TYPE_STRING,
+ "dbi_result_get_string_copy_idx() called for non-string "
+ "column type %u", current_query->field_types[i - 1]);
+ if (! get_golden_data(res, i).string)
+ return NULL;
+ return strdup(get_golden_data(res, i).string);
+} /* dbi_result_get_string_copy_idx */
+
+time_t
+dbi_result_get_datetime_idx(dbi_result res, unsigned int i)
+{
+ fail_unless(current_query->field_types[i - 1] == DBI_TYPE_DATETIME,
+ "dbi_result_get_datetime_idx() called for non-datetime "
+ "column type %u", current_query->field_types[i - 1]);
+ return get_golden_data(res, i).datetime;
+} /* dbi_result_get_datetime_idx */
+
+size_t
+dbi_result_get_field_length_idx(dbi_result res, unsigned int i)
+{
+ /* this will check if the parameters are valid */
+ get_golden_data(res, i);
+
+ switch (current_query->field_types[i - 1]) {
+ case DBI_TYPE_INTEGER:
+ return sizeof(long long);
+ break;
+ case DBI_TYPE_DECIMAL:
+ return sizeof(double);
+ break;
+ case DBI_TYPE_STRING:
+ return strlen(get_golden_data(res, i).string) + 1;
+ break;
+ case DBI_TYPE_DATETIME:
+ return sizeof(time_t);
+ break;
+ case DBI_TYPE_BINARY:
+ return get_golden_data(res, i).binary.length;
+ break;
+ }
+
+ fail("INTERNAL ERROR: dbi_result_get_field_length_idx() "
+ "called for unexpected field type %u",
+ current_query->field_types[i - 1]);
+ return 0;
+} /* dbi_result_get_field_length_idx */
+
+const unsigned char *
+dbi_result_get_binary_idx(dbi_result res, unsigned int i)
+{
+ fail_unless(current_query->field_types[i - 1] == DBI_TYPE_BINARY,
+ "dbi_result_get_binary_idx() called for non-binary "
+ "column type %u", current_query->field_types[i - 1]);
+ return get_golden_data(res, i).binary.datum;
+} /* dbi_result_get_binary_idx */
+
+unsigned char *
+dbi_result_get_binary_copy_idx(dbi_result res, unsigned int i)
+{
+ const char *data;
+ fail_unless(current_query->field_types[i - 1] == DBI_TYPE_BINARY,
+ "dbi_result_get_binary_copy_idx() called for non-binary "
+ "column type %u", current_query->field_types[i - 1]);
+ data = (const char *)get_golden_data(res, i).binary.datum;
+ if (! data)
+ return NULL;
+ return (unsigned char *)strdup(data);
+} /* dbi_result_get_binary_copy_idx */
+
+static unsigned long long dbi_result_free_called = 0;
+int
+dbi_result_free(dbi_result res)
+{
+ mock_query_t *q = res;
+
+ ++dbi_result_free_called;
+ if (! q)
+ return -1;
+
+ current_query = NULL;
+ return 0;
+} /* dbi_result_free */
+
/*
* private helper functions
*/
client = sdb_dbi_client_create("mockdriver", "mockdatabase");
fail_unless(client != NULL,
"sdb_dbi_client_create() = NULL; expected client object");
+
+ dbi_conn_connect_called = 0;
} /* setup */
static void
client = NULL;
} /* teardown */
-static int test_query_callback_called = 0;
+static unsigned long long query_callback_called = 0;
static int
-test_query_callback(sdb_dbi_client_t *c,
+query_callback(sdb_dbi_client_t *c,
size_t n, sdb_data_t *data, sdb_object_t *user_data)
{
- ++test_query_callback_called;
+ size_t i;
+
+ ++query_callback_called;
fail_unless(c == client,
"query callback received unexpected client = %p; "
"expected: %p", c, client);
- fail_unless(n == 0,
- "query callback received n = %i; expected: 0", n);
- fail_unless(data == NULL,
- "query callback received data = %p; expected: NULL", data);
+ fail_unless(n == current_query->nfields,
+ "query callback received n = %i; expected: %i",
+ n, current_query->nfields);
+ fail_unless(data != NULL,
+ "query callback received data = NULL; expected: valid data");
fail_unless(user_data == TEST_MAGIC,
"query callback received user_data = %p; expected: %p",
user_data, TEST_MAGIC);
+
+ for (i = 0; i < n; ++i) {
+ int expected_type = DBI_TYPE_TO_SC(current_query->field_types[i]);
+ mock_data_t expected_data;
+
+ fail_unless((int)data[i].type == expected_type,
+ "query callback received unexpected type %i for "
+ "column %zu; expected: %i", data[i].type, i,
+ expected_type);
+
+ expected_data = golden_data[current_query->current_row - 1][i];
+ switch (expected_type) {
+ case SDB_TYPE_INTEGER:
+ fail_unless(data[i].data.integer == expected_data.integer,
+ "query callback received unexpected data %lli "
+ "for column %zu; expected: %lli",
+ data[i].data.integer, i, expected_data.integer);
+ break;
+ case SDB_TYPE_DECIMAL:
+ fail_unless(data[i].data.decimal == expected_data.decimal,
+ "query callback received unexpected data %g "
+ "for column %zu; expected: %g",
+ data[i].data.decimal, i, expected_data.decimal);
+ break;
+ case SDB_TYPE_STRING:
+ fail_unless(!strcmp(data[i].data.string, expected_data.string),
+ "query callback received unexpected data %s "
+ "for column %zu; expected: %s",
+ data[i].data.string, i, expected_data.string);
+ break;
+ case SDB_TYPE_DATETIME:
+ fail_unless(data[i].data.datetime
+ == SECS_TO_SDB_TIME(expected_data.datetime),
+ "query callback received unexpected data "PRIscTIME
+ " for column %zu; expected: "PRIscTIME,
+ data[i].data.integer, i,
+ SECS_TO_SDB_TIME(expected_data.integer));
+ break;
+ case SDB_TYPE_BINARY:
+ fail_unless(data[i].data.binary.length ==
+ expected_data.binary.length,
+ "query callback received unexpected "
+ "binary data length %zu for column %zu; "
+ "expected: %lli", data[i].data.binary.length, i,
+ expected_data.binary.length);
+ fail_unless(!memcmp(data[i].data.binary.datum,
+ expected_data.binary.datum,
+ expected_data.binary.length),
+ "query callback received unexpected binary data %p "
+ "for column %zu; expected: %p",
+ data[i].data.binary.datum, i,
+ expected_data.binary.datum);
+ break;
+ default:
+ fail("INTERNAL ERROR: query callback received "
+ "unknown type %i for column %zu",
+ expected_type, i);
+ }
+ }
return 0;
-} /* test_query_callback */
+} /* query_callback */
/*
* tests
*/
-START_TEST(test_client_connect)
+START_TEST(test_sdb_dbi_client_connect)
{
int check = sdb_dbi_client_connect(client);
fail_unless(check == 0,
}
END_TEST
-START_TEST(test_client_check_conn)
+START_TEST(test_sdb_dbi_client_check_conn)
{
int check = sdb_dbi_client_check_conn(client);
fail_unless(check == 0,
"sdb_dbi_client_check_conn() called dbi_conn_connect %i times; "
"expected: 1", dbi_conn_connect_called);
+ dbi_conn_connect_called = 0;
check = sdb_dbi_client_check_conn(client);
fail_unless(check == 0,
"sdb_dbi_client_check_conn() = %i; expected: 0", check);
/* should not reconnect */
- fail_unless(dbi_conn_connect_called == 1,
+ fail_unless(dbi_conn_connect_called == 0,
"sdb_dbi_client_check_conn() called dbi_conn_connect %i time(s); "
- "expected: 0", dbi_conn_connect_called - 1);
+ "expected: 0", dbi_conn_connect_called);
}
END_TEST
-START_TEST(test_exec_query)
+START_TEST(test_sdb_dbi_exec_query)
{
- int check = sdb_dbi_exec_query(client, "mockquery", test_query_callback,
+ size_t i;
+
+ int check = sdb_dbi_exec_query(client, "mockquery0", query_callback,
/* user_data = */ TEST_MAGIC, /* n = */ 0);
/* not connected yet */
fail_unless(check < 0,
connect();
- check = sdb_dbi_exec_query(client, "mockquery", test_query_callback,
- /* user_data = */ TEST_MAGIC, /* n = */ 0);
- fail_unless(check == 0,
- "sdb_dbi_exec_query() = %i; expected: 0", check);
-
- fail_unless(dbi_conn_query_called == 1,
- "sdb_dbi_exec_query() called dbi_conn_query %i times; "
- "expected: 1", dbi_conn_query_called);
- fail_unless(test_query_callback_called == 0,
- "sdb_dbi_exec_query() did not call the registered callback "
- "for each result row; got %i call%s; expected: 0",
- test_query_callback_called,
- (test_query_callback_called == 1) ? "" : "s");
-
- fail_unless(dbi_result_free_called == 1,
- "sdb_dbi_exec_query() did not free the query result object");
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(mock_queries); ++i) {
+ mock_query_t *q = &mock_queries[i];
+
+ unsigned long long expected_callback_calls = 0;
+
+ dbi_conn_query_called = 0;
+ query_callback_called = 0;
+ dbi_result_free_called = 0;
+
+ /* sdb_dbi_exec_query will only use as many type arguments are needed,
+ * so we can safely pass in the maximum number of arguments required
+ * on each call */
+ check = sdb_dbi_exec_query(client, q->name, query_callback,
+ /* user_data = */ TEST_MAGIC, /* n = */ (int)q->nfields,
+ SDB_TYPE_INTEGER, SDB_TYPE_DECIMAL, SDB_TYPE_STRING,
+ SDB_TYPE_DATETIME, SDB_TYPE_BINARY);
+ fail_unless(check == 0,
+ "sdb_dbi_exec_query() = %i; expected: 0", check);
+
+ fail_unless(dbi_conn_query_called == 1,
+ "sdb_dbi_exec_query() called dbi_conn_query %i times; "
+ "expected: 1", dbi_conn_query_called);
+
+ if (q->nfields)
+ expected_callback_calls = q->nrows;
+
+ fail_unless(query_callback_called == expected_callback_calls,
+ "sdb_dbi_exec_query() did not call the registered callback "
+ "for each result row; got %i call%s; expected: 0",
+ query_callback_called,
+ (query_callback_called == 1) ? "" : "s");
+
+ fail_unless(dbi_result_free_called == 1,
+ "sdb_dbi_exec_query() did not free the query result object");
+ }
}
END_TEST
tc = tcase_create("core");
tcase_add_checked_fixture(tc, setup, teardown);
- tcase_add_test(tc, test_client_connect);
- tcase_add_test(tc, test_client_check_conn);
- tcase_add_test(tc, test_exec_query);
+ tcase_add_test(tc, test_sdb_dbi_client_connect);
+ tcase_add_test(tc, test_sdb_dbi_client_check_conn);
+ tcase_add_test(tc, test_sdb_dbi_exec_query);
suite_add_tcase(s, tc);
return s;