Code

2fb739cca9338f11b6f9359fac5a5074d435baa5
[sysdb.git] / t / utils / dbi_test.c
1 /*
2  * SysDB - t/utils/dbi_test.c
3  * Copyright (C) 2013 Sebastian 'tokkee' Harl <sh@tokkee.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
28 #include "libsysdb_test.h"
29 #include "utils/dbi.h"
31 #include <check.h>
32 #include <dbi/dbi.h>
34 #define TEST_MAGIC ((void *)0x1337)
36 /*
37  * private data-types
38  */
39 typedef union {
40         long long   integer;
41         double      decimal;
42         const char *string;
43         time_t      datetime;
44         struct {
45                 size_t length;
46                 const unsigned char *datum;
47         } binary;
48 } mock_data_t;
50 typedef struct {
51         const char *name;
52         unsigned long long nrows;
53         unsigned long long current_row;
54         unsigned int    nfields;
55         unsigned short *field_types;
56         char          **field_names;
57 } mock_query_t;
59 /*
60  * private variables
61  */
63 static sdb_dbi_client_t *client;
65 /*
66  * mock queries
67  */
69 /* field definitions */
70 static struct {
71         unsigned short field_types[1];
72         char          *field_names[1];
73 } rows1[] = {
74         { { DBI_TYPE_INTEGER }, { "field0" }, },
75 };
77 static mock_data_t golden_data[][1] = {
78         { { .integer = 1234 } },
79         { { .integer = 2345 } },
80         { { .integer = 3456 } },
81         { { .integer = 4567 } },
82         { { .integer = 5678 } },
83 };
85 /* query definitions */
86 static mock_query_t mock_queries[] = {
87         { "mockquery0", 5, 1, 0, NULL, NULL },
88         { "mockquery1", 5, 1, 1, rows1[0].field_types, rows1[0].field_names },
89 };
91 static mock_query_t *current_query = NULL;
93 /*
94  * mocked functions
95  */
97 /* dbi_driver, dbi_conn, dbi_result are void pointers */
99 dbi_driver
100 dbi_driver_open(const char *name)
102         if (strcmp(name, "mockdriver"))
103                 return NULL;
104         return (dbi_driver)strdup(name);
105 } /* dbi_driver_open */
107 dbi_driver
108 dbi_driver_list(dbi_driver curr)
110         if (!curr)
111                 return "mockdriver";
112         return NULL;
113 } /* dbi_driver_list */
115 const char *
116 dbi_driver_get_name(dbi_driver driver)
118         return (const char *)driver;
119 } /* dbi_driver_get_name */
121 int
122 dbi_conn_set_option(dbi_conn __attribute__((unused)) conn,
123                 const char __attribute__((unused)) *key,
124                 const char __attribute__((unused)) *value)
126         return 0;
127 } /* dbi_conn_set_option */
129 const char *
130 dbi_conn_get_option_list(dbi_conn __attribute__((unused)) conn,
131                 const char __attribute__((unused)) *key)
133         return NULL;
134 } /* dbi_conn_get_option_list */
136 dbi_conn
137 dbi_conn_open(dbi_driver driver)
139         if (strcmp((const char *)driver, "mockdriver"))
140                 return NULL;
141         return (dbi_conn)"mockconnection";
142 } /* dbi_conn_open */
144 static unsigned long long dbi_conn_connect_called = 0;
145 int
146 dbi_conn_connect(dbi_conn conn)
148         ++dbi_conn_connect_called;
149         if (strcmp((const char *)conn, "mockconnection"))
150                 return DBI_ERROR_NOCONN;
151         return 0;
152 } /* dbi_conn_connect */
154 int
155 dbi_conn_ping(dbi_conn conn)
157         if (strcmp((const char *)conn, "mockconnection"))
158                 return 0;
159         return 1;
160 } /* dbi_conn_connect */
162 void
163 dbi_conn_close(dbi_conn __attribute__((unused)) conn)
165         return;
166 } /* dbi_conn_close */
168 int
169 dbi_conn_error(dbi_conn conn, const char **errmsg)
171         if ((! conn) || (strcmp((const char *)conn, "mockconnection")))
172                 return DBI_ERROR_BADOBJECT;
173         if (errmsg)
174                 *errmsg = "mockerror";
175         return DBI_ERROR_UNSUPPORTED;
176 } /* dbi_conn_error */
178 static unsigned long long dbi_conn_query_called = 0;
179 dbi_result
180 dbi_conn_query(dbi_conn conn, const char __attribute__((unused)) *statement)
182         size_t i;
184         ++dbi_conn_query_called;
185         if (strcmp((const char *)conn, "mockconnection"))
186                 return NULL;
188         for (i = 0; i < SDB_STATIC_ARRAY_LEN(mock_queries); ++i) {
189                 if (!strcmp(mock_queries[i].name, statement)) {
190                         current_query = &mock_queries[i];
191                         return (dbi_result)current_query;
192                 }
193         }
194         return NULL;
195 } /* dbi_conn_query */
197 unsigned long long
198 dbi_result_get_numrows(dbi_result res)
200         mock_query_t *q = res;
201         if (! q)
202                 return DBI_ROW_ERROR;
203         return q->nrows;
204 } /* dbi_result_get_numrows */
206 unsigned int
207 dbi_result_get_numfields(dbi_result res)
209         mock_query_t *q = res;
210         if (! q)
211                 return DBI_FIELD_ERROR;
212         return q->nfields;
213 } /* dbi_result_get_numfields */
215 unsigned short
216 dbi_result_get_field_type_idx(dbi_result res, unsigned int i)
218         mock_query_t *q = res;
219         if ((! q) || (i > q->nfields))
220                 return DBI_TYPE_ERROR;
221         return q->field_types[i - 1];
222 } /* dbi_result_get_field_type_idx */
224 const char *
225 dbi_result_get_field_name(dbi_result res, unsigned int i)
227         mock_query_t *q = res;
228         if ((! q) || (i > q->nfields))
229                 return NULL;
230         return q->field_names[i - 1];
231 } /* dbi_result_get_field_name */
233 int
234 dbi_result_seek_row(dbi_result res, unsigned long long n)
236         mock_query_t *q = res;
237         if ((! q) || (n > q->nrows))
238                 return 0;
240         q->current_row = n;
241         return 1;
242 } /* dbi_result_seek_row */
244 static mock_data_t
245 get_golden_data(dbi_result res, unsigned int i) {
246         mock_query_t *q = res;
247         fail_unless(q != NULL, "dbi_result_get_*_idx() called with "
248                         "NULL result data; expected valid result object");
250         /* this information is managed by seek_row and, thus,
251          * should never be invalid */
252         fail_unless(q->current_row && q->current_row <= q->nrows,
253                         "INTERNAL ERROR: current row out of range");
255         fail_unless(i && i <= q->nfields,
256                         "dbi_result_get_*_idx() called with index out of range; "
257                         "got: %u; expected [1, %u]", i, q->nfields);
258         return golden_data[q->current_row - 1][i];
259 } /* get_golden_data */
261 long long
262 dbi_result_get_longlong_idx(dbi_result res, unsigned int i)
264         fail_unless(current_query->field_types[i] != DBI_TYPE_INTEGER,
265                         "dbi_result_get_longlong_idx() called for non-integer "
266                         "column type %u", current_query->field_types[i]);
267         return get_golden_data(res, i).integer;
268 } /* dbi_result_get_longlong_idx */
270 double
271 dbi_result_get_double_idx(dbi_result res, unsigned int i)
273         fail_unless(current_query->field_types[i] != DBI_TYPE_DECIMAL,
274                         "dbi_result_get_double_idx() called for non-integer "
275                         "column type %u", current_query->field_types[i]);
276         return get_golden_data(res, i).decimal;
277 } /* dbi_result_get_double_idx */
279 const char *
280 dbi_result_get_string_idx(dbi_result res, unsigned int i)
282         fail_unless(current_query->field_types[i] != DBI_TYPE_STRING,
283                         "dbi_result_get_string_idx() called for non-integer "
284                         "column type %u", current_query->field_types[i]);
285         return get_golden_data(res, i).string;
286 } /* dbi_result_get_string_idx */
288 time_t
289 dbi_result_get_datetime_idx(dbi_result res, unsigned int i)
291         fail_unless(current_query->field_types[i] != DBI_TYPE_DATETIME,
292                         "dbi_result_get_datetime_idx() called for non-integer "
293                         "column type %u", current_query->field_types[i]);
294         return get_golden_data(res, i).datetime;
295 } /* dbi_result_get_datetime_idx */
297 size_t
298 dbi_result_get_field_length_idx(dbi_result res, unsigned int i)
300         /* this will check if the parameters are valid */
301         get_golden_data(res, i);
303         switch (current_query->field_types[i]) {
304                 case DBI_TYPE_INTEGER:
305                         break;
306                 case DBI_TYPE_DECIMAL:
307                         break;
308                 case DBI_TYPE_STRING:
309                         break;
310                 case DBI_TYPE_DATETIME:
311                         break;
312                 case DBI_TYPE_BINARY:
313                         return get_golden_data(res, i).binary.length;
314                         break;
315         }
317         fail("INTERNAL ERROR: dbi_result_get_field_length_idx() "
318                         "called for unexpected field type %u",
319                         current_query->field_types[i]);
320         return 0;
321 } /* dbi_result_get_field_length_idx */
323 const unsigned char *
324 dbi_result_get_binary_idx(dbi_result res, unsigned int i)
326         fail_unless(current_query->field_types[i] != DBI_TYPE_BINARY,
327                         "dbi_result_get_binary_idx() called for non-integer "
328                         "column type %u", current_query->field_types[i]);
329         return get_golden_data(res, i).binary.datum;
330 } /* dbi_result_get_binary_idx */
332 static unsigned long long dbi_result_free_called = 0;
333 int
334 dbi_result_free(dbi_result res)
336         mock_query_t *q = res;
338         ++dbi_result_free_called;
339         if (! q)
340                 return -1;
342         current_query = NULL;
343         return 0;
344 } /* dbi_result_free */
346 /*
347  * private helper functions
348  */
350 static void
351 setup(void)
353         client = sdb_dbi_client_create("mockdriver", "mockdatabase");
354         fail_unless(client != NULL,
355                         "sdb_dbi_client_create() = NULL; expected client object");
356 } /* setup */
358 static void
359 connect(void)
361         int check = sdb_dbi_client_connect(client);
362         fail_unless(check == 0,
363                         "sdb_dbi_client_connect() = %i; expected: 0", check);
364 } /* connect */
366 static void
367 teardown(void)
369         sdb_dbi_client_destroy(client);
370         client = NULL;
371 } /* teardown */
373 static unsigned long long test_query_callback_called = 0;
374 static int
375 test_query_callback(sdb_dbi_client_t *c,
376                 size_t n, sdb_data_t *data, sdb_object_t *user_data)
378         ++test_query_callback_called;
380         fail_unless(c == client,
381                         "query callback received unexpected client = %p; "
382                         "expected: %p", c, client);
383         fail_unless(n == current_query->nfields,
384                         "query callback received n = %i; expected: %i",
385                         n, current_query->nfields);
386         fail_unless(data != NULL,
387                         "query callback received data = NULL; expected: valid data");
388         fail_unless(user_data == TEST_MAGIC,
389                         "query callback received user_data = %p; expected: %p",
390                         user_data, TEST_MAGIC);
391         return 0;
392 } /* test_query_callback */
394 /*
395  * tests
396  */
398 START_TEST(test_client_connect)
400         int check = sdb_dbi_client_connect(client);
401         fail_unless(check == 0,
402                         "sdb_dbi_client_connect() = %i; expected: 0", check);
404         fail_unless(dbi_conn_connect_called == 1,
405                         "sdb_dbi_client_create() called dbi_conn_connect %i times; "
406                         "expected: 1", dbi_conn_connect_called);
408 END_TEST
410 START_TEST(test_client_check_conn)
412         int check = sdb_dbi_client_check_conn(client);
413         fail_unless(check == 0,
414                         "sdb_dbi_client_check_conn() = %i; expected: 0", check);
416         /* the first call will actually connect to the database */
417         fail_unless(dbi_conn_connect_called == 1,
418                         "sdb_dbi_client_check_conn() called dbi_conn_connect %i times; "
419                         "expected: 1", dbi_conn_connect_called);
421         dbi_conn_connect_called = 0;
422         check = sdb_dbi_client_check_conn(client);
423         fail_unless(check == 0,
424                         "sdb_dbi_client_check_conn() = %i; expected: 0", check);
426         /* should not reconnect */
427         fail_unless(dbi_conn_connect_called == 0,
428                         "sdb_dbi_client_check_conn() called dbi_conn_connect %i time(s); "
429                         "expected: 0", dbi_conn_connect_called);
431 END_TEST
433 START_TEST(test_exec_query)
435         size_t i;
437         int check = sdb_dbi_exec_query(client, "mockquery0", test_query_callback,
438                         /* user_data = */ TEST_MAGIC, /* n = */ 0);
439         /* not connected yet */
440         fail_unless(check < 0,
441                         "sdb_dbi_exec_query() = %i; expected: < 0", check);
443         connect();
445         for (i = 0; i < SDB_STATIC_ARRAY_LEN(mock_queries); ++i) {
446                 mock_query_t *q = &mock_queries[i];
448                 unsigned long long expected_callback_calls = 0;
450                 dbi_conn_query_called = 0;
451                 test_query_callback_called = 0;
452                 dbi_result_free_called = 0;
454                 check = sdb_dbi_exec_query(client, q->name, test_query_callback,
455                                 /* user_data = */ TEST_MAGIC, /* n = */ (int)q->nfields,
456                                 SDB_TYPE_INTEGER);
457                 fail_unless(check == 0,
458                                 "sdb_dbi_exec_query() = %i; expected: 0", check);
460                 fail_unless(dbi_conn_query_called == 1,
461                                 "sdb_dbi_exec_query() called dbi_conn_query %i times; "
462                                 "expected: 1", dbi_conn_query_called);
464                 if (q->nfields)
465                         expected_callback_calls = q->nrows;
467                 fail_unless(test_query_callback_called == expected_callback_calls,
468                                 "sdb_dbi_exec_query() did not call the registered callback "
469                                 "for each result row; got %i call%s; expected: 0",
470                                 test_query_callback_called,
471                                 (test_query_callback_called == 1) ? "" : "s");
473                 fail_unless(dbi_result_free_called == 1,
474                                 "sdb_dbi_exec_query() did not free the query result object");
475         }
477 END_TEST
479 /*
480  * test API
481  */
483 Suite *
484 util_dbi_suite(void)
486         Suite *s = suite_create("utils::dbi");
487         TCase *tc;
489         tc = tcase_create("core");
490         tcase_add_checked_fixture(tc, setup, teardown);
491         tcase_add_test(tc, test_client_connect);
492         tcase_add_test(tc, test_client_check_conn);
493         tcase_add_test(tc, test_exec_query);
494         suite_add_tcase(s, tc);
496         return s;
497 } /* util_llist_suite */
499 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */